From cc2e093a4b74d5f1be1251cdbe8797274964e602 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Tue, 11 Mar 2025 15:13:17 +0700 Subject: [PATCH 01/49] feat: use llama.cpp server --- engine/CMakeLists.txt | 1 + engine/cli/CMakeLists.txt | 1 + engine/cli/command_line_parser.cc | 20 +- engine/cli/commands/engine_install_cmd.cc | 5 +- engine/cli/commands/server_start_cmd.cc | 26 +- engine/cli/main.cc | 2 +- engine/cli/utils/download_progress.cc | 4 +- engine/controllers/engines.cc | 8 +- engine/cortex-common/local_enginei.h | 32 ++ engine/cortex-common/remote_enginei.h | 2 - .../extensions/local-engine/local_engine.cc | 474 ++++++++++++++++++ engine/extensions/local-engine/local_engine.h | 51 ++ engine/main.cc | 13 +- engine/services/engine_service.cc | 137 ++++- engine/services/engine_service.h | 19 +- engine/services/hardware_service.cc | 5 +- engine/services/inference_service.cc | 55 +- engine/utils/cuda_toolkit_utils.h | 29 +- engine/utils/engine_constants.h | 4 +- engine/utils/engine_matcher_utils.h | 13 +- engine/utils/github_release_utils.h | 6 +- engine/utils/system_info_utils.h | 2 +- 22 files changed, 790 insertions(+), 119 deletions(-) create mode 100644 engine/cortex-common/local_enginei.h create mode 100644 engine/extensions/local-engine/local_engine.cc create mode 100644 engine/extensions/local-engine/local_engine.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 3f08f83e0..bc1b7ceec 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -178,6 +178,7 @@ add_executable(${TARGET_NAME} main.cc ${CMAKE_CURRENT_SOURCE_DIR}/utils/process/utils.cc ${CMAKE_CURRENT_SOURCE_DIR}/extensions/remote-engine/remote_engine.cc + ${CMAKE_CURRENT_SOURCE_DIR}/extensions/local-engine/local_engine.cc ) diff --git a/engine/cli/CMakeLists.txt b/engine/cli/CMakeLists.txt index 0162c1f56..d292fd57c 100644 --- a/engine/cli/CMakeLists.txt +++ b/engine/cli/CMakeLists.txt @@ -86,6 +86,7 @@ add_executable(${TARGET_NAME} main.cc ${CMAKE_CURRENT_SOURCE_DIR}/../services/hardware_service.cc ${CMAKE_CURRENT_SOURCE_DIR}/../services/database_service.cc ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/remote-engine/remote_engine.cc + ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/local-engine/local_engine.cc ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/python-engine/python_engine.cc ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/template_renderer.cc diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index b423a6896..af656248f 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -33,6 +33,7 @@ #include "services/engine_service.h" #include "utils/file_manager_utils.h" #include "utils/logging_utils.h" +#include "utils/task_queue.h" namespace { constexpr const auto kCommonCommandsGroup = "Common Commands"; @@ -51,7 +52,8 @@ CommandLineParser::CommandLineParser() dylib_path_manager_{std::make_shared()}, db_service_{std::make_shared()}, engine_service_{std::make_shared( - download_service_, dylib_path_manager_, db_service_)} { + download_service_, dylib_path_manager_, db_service_, + std::make_shared(1, "q"))} { supported_engines_ = engine_service_->GetSupportedEngineNames().value(); } @@ -124,14 +126,14 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) { } } #endif - auto config = file_manager_utils::GetCortexConfig(); - if (!config.llamacppVersion.empty() && - config.latestLlamacppRelease != config.llamacppVersion) { - CLI_LOG( - "\nNew llama.cpp version available: " << config.latestLlamacppRelease); - CLI_LOG("To update, run: " << commands::GetCortexBinary() - << " engines update llama-cpp"); - } + // auto config = file_manager_utils::GetCortexConfig(); + // if (!config.llamacppVersion.empty() && + // config.latestLlamacppRelease != config.llamacppVersion) { + // CLI_LOG( + // "\nNew llama.cpp version available: " << config.latestLlamacppRelease); + // CLI_LOG("To update, run: " << commands::GetCortexBinary() + // << " engines update llama-cpp"); + // } return true; } diff --git a/engine/cli/commands/engine_install_cmd.cc b/engine/cli/commands/engine_install_cmd.cc index 85a5def5d..79e524996 100644 --- a/engine/cli/commands/engine_install_cmd.cc +++ b/engine/cli/commands/engine_install_cmd.cc @@ -90,7 +90,10 @@ bool EngineInstallCmd::Exec(const std::string& engine, std::vector variant_selections; for (const auto& variant : variant_result.value()) { auto v_name = variant["name"].asString(); - if (string_utils::StringContainsIgnoreCase(v_name, hw_inf_.sys_inf->os) && + if ((string_utils::StringContainsIgnoreCase(v_name, + hw_inf_.sys_inf->os) || + (hw_inf_.sys_inf->os == "linux" && + string_utils::StringContainsIgnoreCase(v_name, "ubuntu"))) && string_utils::StringContainsIgnoreCase(v_name, hw_inf_.sys_inf->arch)) { variant_selections.push_back(variant["name"].asString()); diff --git a/engine/cli/commands/server_start_cmd.cc b/engine/cli/commands/server_start_cmd.cc index a4bcb1eb5..7ef68d892 100644 --- a/engine/cli/commands/server_start_cmd.cc +++ b/engine/cli/commands/server_start_cmd.cc @@ -66,7 +66,7 @@ bool ServerStartCmd::Exec(const std::string& host, int port, si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); std::wstring params = L"--start-server"; - params += L" --config_file_path \"" + + params += L" --config_file_path \"" + file_manager_utils::GetConfigurationPath().wstring() + L"\""; params += L" --data_folder_path \"" + file_manager_utils::GetCortexDataPath().wstring() + L"\""; @@ -80,17 +80,17 @@ bool ServerStartCmd::Exec(const std::string& host, int port, mutable_cmds.push_back(L'\0'); // Create child process if (!CreateProcess( - NULL, // No module name (use command line) + NULL, // No module name (use command line) mutable_cmds - .data(), // Command line (replace with your actual executable) - NULL, // Process handle not inheritable - NULL, // Thread handle not inheritable - FALSE, // Set handle inheritance - CREATE_NO_WINDOW, // No new console - NULL, // Use parent's environment block - NULL, // Use parent's starting directory - &si, // Pointer to STARTUPINFO structure - &pi)) // Pointer to PROCESS_INFORMATION structure + .data(), // Command line (replace with your actual executable) + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance + CREATE_NO_WINDOW, // No new console + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi)) // Pointer to PROCESS_INFORMATION structure { std::cout << "Could not start server: " << GetLastError() << std::endl; return false; @@ -109,7 +109,9 @@ bool ServerStartCmd::Exec(const std::string& host, int port, auto download_srv = std::make_shared(); auto dylib_path_mng = std::make_shared(); auto db_srv = std::make_shared(); - EngineService(download_srv, dylib_path_mng, db_srv).RegisterEngineLibPath(); + EngineService(download_srv, dylib_path_mng, db_srv, + std::make_shared(1, "task_queue")) + .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() + "/" + exe; commands.push_back(p); diff --git a/engine/cli/main.cc b/engine/cli/main.cc index 66f14c116..28b4e8edd 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -147,7 +147,7 @@ int main(int argc, char* argv[]) { should_check_for_latest_llamacpp_version = now > last_check; } - if (should_check_for_latest_llamacpp_version) { + if (false) { std::thread t1([]() { // TODO: namh current we only check for llamacpp. Need to add support for other engine auto get_latest_version = []() -> cpp::result { diff --git a/engine/cli/utils/download_progress.cc b/engine/cli/utils/download_progress.cc index 07f91adb4..02b0ff51a 100644 --- a/engine/cli/utils/download_progress.cc +++ b/engine/cli/utils/download_progress.cc @@ -83,8 +83,8 @@ bool DownloadProgress::Handle( size_t max_length = 20) -> std::string { // Check the length of the input string if (str.length() >= max_length) { - return str.substr( - 0, max_length); // Return truncated string if it's too long + return str.substr(0, max_length - 3) + + ".. "; // Return truncated string if it's too long } // Calculate the number of spaces needed diff --git a/engine/controllers/engines.cc b/engine/controllers/engines.cc index 43bc3735f..45a6802f3 100644 --- a/engine/controllers/engines.cc +++ b/engine/controllers/engines.cc @@ -5,9 +5,9 @@ #include "utils/engine_constants.h" #include "utils/http_util.h" #include "utils/logging_utils.h" +#include "utils/normalize_engine.h" #include "utils/scope_exit.h" #include "utils/string_utils.h" -#include "utils/normalize_engine.h" void Engines::ListEngine( const HttpRequestPtr& req, @@ -146,11 +146,13 @@ void Engines::GetEngineVariants( auto normalize_version = string_utils::RemoveSubstring(version, "v"); Json::Value releases(Json::arrayValue); for (const auto& release : result.value()) { - auto json = release.ToApiJson(cortex::engine::NormalizeEngine(engine), normalize_version); + auto json = release.ToApiJson(cortex::engine::NormalizeEngine(engine), + normalize_version); if (json != std::nullopt) { releases.append(json.value()); } } + CTL_INF(releases.toStyledString()); auto resp = cortex_utils::CreateCortexHttpJsonResponse(releases); resp->setStatusCode(k200OK); callback(resp); @@ -173,6 +175,8 @@ void Engines::InstallEngine( } norm_version = version; } + CTL_INF("version: " << norm_version + << ", norm_variant: " << norm_variant.value_or("")); auto result = engine_service_->InstallEngineAsync(engine, norm_version, norm_variant); diff --git a/engine/cortex-common/local_enginei.h b/engine/cortex-common/local_enginei.h new file mode 100644 index 000000000..18d2a1727 --- /dev/null +++ b/engine/cortex-common/local_enginei.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include "json/value.h" +#include "trantor/utils/Logger.h" +class LocalEngineI { + public: + virtual ~LocalEngineI() {} + + virtual void HandleChatCompletion( + std::shared_ptr json_body, + std::function&& callback) = 0; + virtual void HandleEmbedding( + std::shared_ptr json_body, + std::function&& callback) = 0; + virtual void LoadModel( + std::shared_ptr json_body, + std::function&& callback) = 0; + virtual void UnloadModel( + std::shared_ptr json_body, + std::function&& callback) = 0; + virtual void GetModelStatus( + std::shared_ptr json_body, + std::function&& callback) = 0; + + // Get list of running models + virtual void GetModels( + std::shared_ptr jsonBody, + std::function&& callback) = 0; +}; \ No newline at end of file diff --git a/engine/cortex-common/remote_enginei.h b/engine/cortex-common/remote_enginei.h index 835f526a0..163490cdc 100644 --- a/engine/cortex-common/remote_enginei.h +++ b/engine/cortex-common/remote_enginei.h @@ -1,7 +1,5 @@ #pragma once -#pragma once - #include #include diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc new file mode 100644 index 000000000..1898b084e --- /dev/null +++ b/engine/extensions/local-engine/local_engine.cc @@ -0,0 +1,474 @@ +#include "local_engine.h" +#include +#include +#include +#include "utils/curl_utils.h" +#include "utils/json_helper.h" +#include "utils/logging_utils.h" +#include "utils/url_parser.h" + +namespace cortex::local { + +namespace { +const std::unordered_set kIgnoredParams = { + "model", "model_alias", "embedding", "ai_prompt", + "ai_template", "prompt_template", "mmproj", "system_prompt", + "created", "stream", "name", "os", + "owned_by", "files", "gpu_arch", "quantization_method", + "engine", "system_template", "max_tokens", "user_template", + "user_prompt", "min_keep", "mirostat", "mirostat_eta", + "mirostat_tau", "text_model", "version", "n_probs", + "object", "penalize_nl", "precision", "size", + "stop", "tfs_z", "typ_p"}; + +const std::unordered_map kParamsMap = { + {"cpu_threads", "--threads"}, + {"n_ubatch", "--ubatch-size"}, + {"n_batch", "--batch-size"}, + {"n_parallel", "--parallel"}, + {"temperature", "--temp"}, + {"top_k", "--top-k"}, + {"top_p", "--top-p"}, + {"min_p", "--min-p"}, + {"dynatemp_exponent", "--dynatemp-exp"}, + {"ctx_len", "--ctx-size"}, + {"ngl", "-ngl"}, +}; + +int GenerateRandomInteger(int min, int max) { + static std::random_device rd; // Seed for the random number engine + static std::mt19937 gen(rd()); // Mersenne Twister random number engine + std::uniform_int_distribution<> dis( + min, max); // Distribution for the desired range + + return dis(gen); // Generate and return a random integer within the range +} + +std::vector ConvertJsonToParamsVector(const Json::Value& root) { + std::vector res; + std::string errors; + + for (const auto& member : root.getMemberNames()) { + if (member == "model_path" || member == "llama_model_path") { + if (!root[member].isNull()) { + res.push_back("--model"); + res.push_back(root[member].asString()); + } + continue; + } else if (kIgnoredParams.find(member) != kIgnoredParams.end()) { + continue; + } else if (kParamsMap.find(member) != kParamsMap.end()) { + res.push_back(kParamsMap.at(member)); + res.push_back(root[member].asString()); + continue; + } else if (member == "model_type") { + if (root[member].asString() == "embedding") { + res.push_back("--embedding"); + } + continue; + } + + res.push_back("--" + member); + if (root[member].isString()) { + res.push_back(root[member].asString()); + } else if (root[member].isInt()) { + res.push_back(std::to_string(root[member].asInt())); + } else if (root[member].isDouble()) { + res.push_back(std::to_string(root[member].asDouble())); + } else if (root[member].isArray()) { + std::stringstream ss; + ss << "["; + bool first = true; + for (const auto& value : root[member]) { + if (!first) { + ss << ", "; + } + ss << "\"" << value.asString() << "\""; + first = false; + } + ss << "] "; + res.push_back(ss.str()); + } + } + + return res; +} + +constexpr const auto kMinDataChunkSize = 6u; + +struct StreamingCallback { + std::shared_ptr> callback; + bool need_stop = true; +}; + +size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { + auto* sc = static_cast(userdata); + size_t data_length = size * nmemb; + + if (ptr && data_length > kMinDataChunkSize) { + std::string chunk(ptr + kMinDataChunkSize, data_length - kMinDataChunkSize); + CTL_DBG(chunk); + if (chunk.find("[DONE]") != std::string::npos) { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + Json::Value chunk_json; + chunk_json["data"] = "data: [DONE]"; + sc->need_stop = false; + (*sc->callback)(std::move(status), std::move(chunk_json)); + return data_length; + } + Json::Value chunk_json; + chunk_json["data"] = "data: " + chunk; + Json::Value status; + status["is_done"] = false; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + (*sc->callback)(std::move(status), std::move(chunk_json)); + } + + return data_length; +} +} // namespace + +LocalEngine::~LocalEngine() { + for (auto& [_, si] : server_map_) { + (void) cortex::process::KillProcess(si.process_info); + } + server_map_.clear(); +} +void LocalEngine::HandleChatCompletion( + std::shared_ptr json_body, + std::function&& callback) { + auto model_id = json_body->get("model", "").asString(); + if (model_id.empty()) { + CTL_WRN("Model is empty"); + } + if (server_map_.find(model_id) != server_map_.end()) { + auto& s = server_map_[model_id]; + bool is_stream = json_body->get("stream", false).asBool(); + if (is_stream) { + q_.RunInQueue([s, json_body, callback] { + auto curl = curl_easy_init(); + if (!curl) { + CTL_WRN("Failed to initialize CURL"); + return; + } + + auto url = "http://" + s.host + ":" + std::to_string(s.port) + + "/v1/chat/completions"; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + auto json_str = json_body->toStyledString(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_str.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json_str.length()); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + + StreamingCallback sc; + sc.callback = + std::make_shared>( + callback); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sc); + auto res = curl_easy_perform(curl); + + if (res != CURLE_OK) { + CTL_WRN("CURL request failed: " << curl_easy_strerror(res)); + + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = true; + status["status_code"] = 500; + + Json::Value error; + error["error"] = curl_easy_strerror(res); + callback(std::move(status), std::move(error)); + } + curl_easy_cleanup(curl); + if (sc.need_stop) { + CTL_DBG("No stop message received, need to stop"); + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + (*sc.callback)(std::move(status), Json::Value()); + } + }); + + } else { + auto url = url_parser::Url{ + .protocol = "http", + .host = s.host + ":" + std::to_string(s.port), + .pathParams = + { + "v1", + "chat", + "completions", + }, + }; + + auto response = curl_utils::SimplePostJson(url.ToFullPath(), + json_body->toStyledString()); + + if (response.has_error()) { + CTL_WRN("Error: " << response.error()); + } else { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(response.value())); + } + } + } else { + Json::Value error; + error["error"] = "Model is not loaded yet: " + model_id; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 400; + callback(std::move(status), std::move(error)); + } +} + +void LocalEngine::HandleEmbedding( + std::shared_ptr json_body, + std::function&& callback) { + auto model_id = json_body->get("model", "").asString(); + if (model_id.empty()) { + CTL_WRN("Model is empty"); + } + if (server_map_.find(model_id) != server_map_.end()) { + auto& s = server_map_[model_id]; + auto url = url_parser::Url{ + .protocol = "http", + .host = s.host + ":" + std::to_string(s.port), + .pathParams = + { + "v1", + "embeddings", + }, + }; + + auto response = curl_utils::SimplePostJson(url.ToFullPath(), + json_body->toStyledString()); + + if (response.has_error()) { + CTL_WRN("Error: " << response.error()); + Json::Value error; + error["error"] = response.error(); + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 400; + callback(std::move(status), std::move(error)); + } else { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(response.value())); + } + } else { + Json::Value error; + error["error"] = "Model is not loaded yet: " + model_id; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 400; + callback(std::move(status), std::move(error)); + } +} + +void LocalEngine::LoadModel( + std::shared_ptr json_body, + std::function&& callback) { + CTL_INF("Start loading model"); + auto wait_for_server_up = [](const std::string& host, int port) { + auto url = url_parser::Url{ + .protocol = "http", + .host = host + ":" + std::to_string(port), + .pathParams = {"health"}, + }; + for (size_t i = 0; i < 10; i++) { + auto res = curl_utils::SimpleGet(url.ToFullPath()); + if (res.has_error()) { + LOG_INFO << "Wait for server up: " << i; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } else { + return true; + } + } + return false; + }; + + LOG_DEBUG << "Start to spawn llama-server"; + auto model_id = json_body->get("model", "").asString(); + if (model_id.empty()) { + CTL_WRN("Model is empty"); + } + server_map_[model_id].host = "127.0.0.1"; + server_map_[model_id].port = GenerateRandomInteger(39400, 39999); + auto& s = server_map_[model_id]; + std::vector params = ConvertJsonToParamsVector(*json_body); + params.push_back("--host"); + params.push_back(s.host); + params.push_back("--port"); + params.push_back(std::to_string(s.port)); + + params.push_back("--pooling"); + params.push_back("mean"); + + std::vector v; + v.reserve(params.size() + 1); + auto engine_dir = engine_service_.GetEngineDirPath("llama.cpp"); + if (engine_dir.has_error()) { + CTL_WRN(engine_dir.error()); + server_map_.erase(model_id); + return; + } + auto exe = (engine_dir.value().first / "llama-server").string(); + + v.push_back(exe); + v.insert(v.end(), params.begin(), params.end()); + engine_service_.RegisterEngineLibPath(); + + auto log_path = + (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); + CTL_INF("log: " << log_path); + auto result = cortex::process::SpawnProcess(v, log_path, log_path); + if (result.has_error()) { + CTL_ERR("Fail to spawn process. " << result.error()); + Json::Value error; + error["error"] = "Fail to spawn process"; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 500; + callback(std::move(status), std::move(error)); + server_map_.erase(model_id); + return; + } + + s.process_info = result.value(); + if (wait_for_server_up(s.host, s.port)) { + Json::Value response; + response["status"] = "Model loaded successfully with pid: " + + std::to_string(s.process_info.pid); + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(response)); + } else { + server_map_.erase(model_id); + Json::Value error; + error["error"] = "Wait for server up timeout"; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 500; + callback(std::move(status), std::move(error)); + } +} + +void LocalEngine::UnloadModel( + std::shared_ptr json_body, + std::function&& callback) { + auto model_id = json_body->get("model", "").asString(); + if (model_id.empty()) { + CTL_WRN("Model is empty"); + } + + if (server_map_.find(model_id) != server_map_.end()) { + auto& s = server_map_[model_id]; +#if defined(_WIN32) || defined(_WIN64) + auto sent = cortex::process::KillProcess(s.process_info); +#else + auto sent = (kill(s.process_info.pid, SIGTERM) != -1); +#endif + if (sent) { + LOG_INFO << "SIGINT signal sent to child process"; + Json::Value response; + response["status"] = "Model unloaded successfully with pid: " + + std::to_string(s.process_info.pid); + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(response)); + server_map_.erase(model_id); + } else { + LOG_ERROR << "Failed to send SIGINT signal to child process"; + Json::Value error; + error["error"] = "Failed to unload model: " + model_id; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 500; + callback(std::move(status), std::move(error)); + } + } else { + Json::Value error; + error["error"] = "Model is not loaded yet: " + model_id; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 400; + callback(std::move(status), std::move(error)); + } +} + +void LocalEngine::GetModelStatus( + std::shared_ptr json_body, + std::function&& callback) { + auto model_id = json_body->get("model", "").asString(); + if (model_id.empty()) { + CTL_WRN("Model is empty"); + } + if (server_map_.find(model_id) != server_map_.end()) { + Json::Value response; + response["status"] = "Model is loaded"; + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(response)); + } else { + Json::Value error; + error["error"] = "Model is not loaded yet: " + model_id; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 400; + callback(std::move(status), std::move(error)); + } +} + +void LocalEngine::GetModels( + std::shared_ptr json_body, + std::function&& callback) {} +} // namespace cortex::local \ No newline at end of file diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h new file mode 100644 index 000000000..b4455f397 --- /dev/null +++ b/engine/extensions/local-engine/local_engine.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include "cortex-common/local_enginei.h" +#include "json/json.h" +#include "services/engine_service.h" +#include "utils/process/utils.h" +#include "utils/task_queue.h" + +namespace cortex::local { +struct ServerAddress { + std::string host; + int port; + cortex::process::ProcessInfo process_info; +}; +class LocalEngine : public LocalEngineI { + public: + LocalEngine(EngineService& engine_service, TaskQueue& q) + : engine_service_(engine_service), q_(q) {} + ~LocalEngine(); + void HandleChatCompletion( + std::shared_ptr json_body, + std::function&& callback) final; + void HandleEmbedding( + std::shared_ptr json_body, + std::function&& callback) final; + void LoadModel( + std::shared_ptr json_body, + std::function&& callback) final; + void UnloadModel( + std::shared_ptr json_body, + std::function&& callback) final; + void GetModelStatus( + std::shared_ptr json_body, + std::function&& callback) final; + + // Get list of running models + void GetModels( + std::shared_ptr jsonBody, + std::function&& callback) final; + + private: + std::unordered_map server_map_; + EngineService& engine_service_; + TaskQueue& q_; +}; + +} // namespace cortex::local \ No newline at end of file diff --git a/engine/main.cc b/engine/main.cc index 492dc9629..bb99f2442 100644 --- a/engine/main.cc +++ b/engine/main.cc @@ -174,15 +174,16 @@ void RunServer(std::optional host, std::optional port, auto config_service = std::make_shared(); auto download_service = std::make_shared(event_queue_ptr, config_service); + auto task_queue = std::make_shared( + std::min(2u, std::thread::hardware_concurrency()), "background_task"); auto engine_service = std::make_shared( - download_service, dylib_path_manager, db_service); + download_service, dylib_path_manager, db_service, task_queue); auto inference_svc = std::make_shared(engine_service); auto model_src_svc = std::make_shared(db_service); - cortex::TaskQueue task_queue( - std::min(2u, std::thread::hardware_concurrency()), "background_task"); - auto model_service = - std::make_shared(db_service, hw_service, download_service, - inference_svc, engine_service, task_queue); + + auto model_service = std::make_shared( + db_service, hw_service, download_service, inference_svc, engine_service, + *task_queue); inference_svc->SetModelService(model_service); auto file_watcher_srv = std::make_shared( diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index c48010122..7c37587f0 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -9,6 +9,7 @@ #include "config/model_config.h" #include "database/engines.h" #include "database/models.h" +#include "extensions/local-engine/local_engine.h" #include "extensions/python-engine/python_engine.h" #include "extensions/remote-engine/remote_engine.h" @@ -18,11 +19,11 @@ #include "utils/file_manager_utils.h" #include "utils/github_release_utils.h" #include "utils/logging_utils.h" +#include "utils/normalize_engine.h" #include "utils/result.hpp" #include "utils/semantic_version_utils.h" #include "utils/system_info_utils.h" #include "utils/url_parser.h" -#include "utils/normalize_engine.h" namespace { std::string GetSuitableCudaVersion(const std::string& engine, @@ -236,11 +237,15 @@ cpp::result EngineService::DownloadEngine( auto latest_version_semantic = normalized_version == "latest" ? res.value()[0].version : normalized_version; - auto merged_variant_name = engine + "-" + latest_version_semantic + "-" + - variant_name.value() + ".tar.gz"; + std::unordered_set merged_variant_name = { + "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + + ".tar.gz", + "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + + ".zip"}; + // CTL_INF("merged_variant_name: " << merged_variant_name); for (const auto& asset : res.value()) { - if (asset.name == merged_variant_name) { + if (merged_variant_name.find(asset.name) != merged_variant_name.end()) { selected_variant = asset; break; } @@ -275,7 +280,8 @@ cpp::result EngineService::DownloadEngine( } } - auto normalize_version = "v" + selected_variant->version; + // auto normalize_version = "v" + selected_variant->version; + auto normalize_version = selected_variant->version; auto variant_folder_name = engine_matcher_utils::GetVariantFromNameAndVersion( selected_variant->name, engine, selected_variant->version); auto variant_folder_path = file_manager_utils::GetEnginesContainerPath() / @@ -295,11 +301,42 @@ cpp::result EngineService::DownloadEngine( auto extract_path = finishedTask.items[0].localPath.parent_path(); archive_utils::ExtractArchive(finishedTask.items[0].localPath.string(), extract_path.string(), true); - + CTL_INF("local path: " << finishedTask.items[0].localPath.string() + << ", extract path: " << extract_path.string()); auto variant = engine_matcher_utils::GetVariantFromNameAndVersion( selected_variant->name, engine, normalize_version); - CTL_INF("Extracted variant: " + variant.value()); + try { + std::ofstream meta(extract_path / "version.txt", std::ios::out); + meta << "name: " << variant.value() << std::endl; + meta << "version: " << normalize_version << std::endl; + meta.close(); + namespace fs = std::filesystem; + + fs::path bin_path = extract_path / "build" / "bin"; + if (fs::exists(bin_path)) { + for (const auto& entry : fs::directory_iterator(bin_path)) { + if (entry.is_regular_file()) { + fs::path target_file = extract_path / entry.path().filename(); + fs::copy_file(entry.path(), target_file, + fs::copy_options::overwrite_existing); + } + } + std::filesystem::remove_all(bin_path.parent_path()); + } + if (!fs::exists(extract_path.parent_path().parent_path() / "deps")) { + fs::create_directory(extract_path.parent_path().parent_path() / "deps"); + } + std::filesystem::permissions(extract_path / "llama-server", + std::filesystem::perms::owner_exec | + std::filesystem::perms::group_exec | + std::filesystem::perms::others_exec, + std::filesystem::perm_options::add); + + } catch (const std::exception& e) { + CTL_INF(e.what()); + } + // set as default auto res = @@ -436,7 +473,26 @@ std::string EngineService::GetMatchedVariant( cpp::result, std::string> EngineService::GetEngineReleases(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - return github_release_utils::GetReleases("janhq", ne); + auto ggml_org = github_release_utils::GetReleases("ggml-org", ne); + auto janhq = github_release_utils::GetReleases("janhq", ne); + if (ggml_org.has_error() && janhq.has_error()) { + return cpp::fail(ggml_org.error()); + } + auto comparator = [](const EngineService::EngineRelease& e1, + const EngineService::EngineRelease& e2) { + return e1.name > e2.name; + }; + std::set s(comparator); + if (ggml_org.has_value()) { + s.insert(ggml_org.value().begin(), ggml_org.value().end()); + } + + if (janhq.has_value()) { + s.insert(janhq.value().begin(), janhq.value().end()); + } + std::vector res; + std::copy(s.begin(), s.end(), std::back_inserter(res)); + return res; } cpp::result, std::string> @@ -444,16 +500,40 @@ EngineService::GetEngineVariants(const std::string& engine, const std::string& version, bool filter_compatible_only) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto engine_release = + auto engine_release_janhq = github_release_utils::GetReleaseByVersion("janhq", ne, version); + auto engine_release_ggml = + github_release_utils::GetReleaseByVersion("ggml-org", ne, version); - if (engine_release.has_error()) { - return cpp::fail("Failed to get engine release: " + engine_release.error()); + if (engine_release_janhq.has_error() && engine_release_ggml.has_error()) { + return cpp::fail("Failed to get engine release: " + + engine_release_janhq.error()); + } + if (engine_release_janhq.has_error()) { + CTL_WRN("Failed to get engine release: " << engine_release_janhq.error()); + } + + if (engine_release_ggml.has_error()) { + CTL_WRN("Failed to get engine release: " << engine_release_ggml.error()); } std::vector compatible_variants; - for (const auto& variant : engine_release.value().assets) { - if (variant.content_type != "application/gzip") { + std::vector assets; + if (engine_release_janhq.has_value()) { + assets.insert(std::end(assets), + std::begin(engine_release_janhq.value().assets), + std::end(engine_release_janhq.value().assets)); + } + if (engine_release_ggml.has_value()) { + assets.insert(std::end(assets), + std::begin(engine_release_ggml.value().assets), + std::end(engine_release_ggml.value().assets)); + } + for (const auto& variant : assets) { + CTL_INF("content_type: " << variant.content_type + << ", name: " << variant.name); + if (variant.content_type != "application/gzip" && + variant.content_type != "application/json; charset=utf-8") { continue; } if (variant.state != "uploaded") { @@ -480,10 +560,11 @@ EngineService::GetEngineVariants(const std::string& engine, name.find("mac") != std::string::npos) os_match = true; if (system_info->os == "windows" && - name.find("windows") != std::string::npos) + name.find("win") != std::string::npos) os_match = true; if (system_info->os == "linux" && - name.find("linux") != std::string::npos) + (name.find("linux") != std::string::npos || + name.find("ubuntu") != std::string::npos)) os_match = true; bool arch_match = false; @@ -491,19 +572,17 @@ EngineService::GetEngineVariants(const std::string& engine, name.find("arm64") != std::string::npos) arch_match = true; if (system_info->arch == "amd64" && - name.find("amd64") != std::string::npos) + name.find("x64") != std::string::npos) arch_match = true; return !(os_match && arch_match); }), compatible_variants.end()); - if (compatible_variants.empty()) { return cpp::fail("No compatible variants found for system " + system_info->os + "/" + system_info->arch); } } - return compatible_variants; } @@ -536,7 +615,7 @@ EngineService::SetDefaultEngineVariant(const std::string& engine, auto normalized_version = string_utils::RemoveSubstring(version, "v"); auto config = file_manager_utils::GetCortexConfig(); - config.llamacppVersion = "v" + normalized_version; + config.llamacppVersion = normalized_version; config.llamacppVariant = variant; auto result = file_manager_utils::UpdateCortexConfig(config); if (result.has_error()) { @@ -560,10 +639,10 @@ cpp::result EngineService::IsEngineVariantReady( return cpp::fail(installed_engines.error()); } - CLI_LOG("IsEngineVariantReady: " << ne << ", " << normalized_version << ", " + CTL_INF("IsEngineVariantReady: " << ne << ", " << normalized_version << ", " << variant); for (const auto& installed_engine : installed_engines.value()) { - CLI_LOG("Installed: name: " + installed_engine.name + + CTL_INF("Installed: name: " + installed_engine.name + ", version: " + installed_engine.version); if (installed_engine.name == variant && installed_engine.version == normalized_version || @@ -627,7 +706,7 @@ EngineService::GetInstalledEngineVariants(const std::string& engine) const { auto node = YAML::LoadFile(version_txt_path.string()); auto ev = EngineVariantResponse{ .name = node["name"].as(), - .version = "v" + node["version"].as(), + .version = node["version"].as(), .engine = engine, }; variants.push_back(ev); @@ -689,6 +768,15 @@ cpp::result EngineService::LoadEngine( } return {}; } + if (engines_.find(ne) == engines_.end()) { + CTL_INF("Loading local engine: " << engine_name); + engines_[ne].engine = new cortex::local::LocalEngine(*this, *(q_.get())); + CTL_INF("Loaded engine: " << engine_name); + } else { + CTL_INF("Engine has already been loaded: " << engine_name); + } + + return {}; // End hard code @@ -789,7 +877,8 @@ void EngineService::RegisterEngineLibPath() { auto reg_result = dylib_path_manager_->RegisterPath(ne, paths); if (reg_result.has_error()) { - CTL_WRN("Failed register lib path for " << engine); + CTL_WRN("Failed register lib path for " + << engine << ", error: " << reg_result.error()); } else { CTL_DBG("Registered lib path for " << engine); } @@ -863,6 +952,8 @@ cpp::result EngineService::UnloadEngine( auto unload_opts = EngineI::EngineUnloadOption{}; e->Unload(unload_opts); delete e; + } else if (std::holds_alternative(engines_[ne].engine)) { + delete std::get(engines_[ne].engine); } else { delete std::get(engines_[ne].engine); } diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index f98037bab..d94bb9de5 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -10,6 +10,7 @@ #include "common/engine_servicei.h" #include "cortex-common/EngineI.h" #include "cortex-common/cortexpythoni.h" +#include "cortex-common/local_enginei.h" #include "cortex-common/remote_enginei.h" #include "database/engines.h" #include "services/database_service.h" @@ -20,6 +21,7 @@ #include "utils/github_release_utils.h" #include "utils/result.hpp" #include "utils/system_info_utils.h" +#include "utils/task_queue.h" struct EngineUpdateResult { std::string engine; @@ -37,7 +39,8 @@ struct EngineUpdateResult { } }; -using EngineV = std::variant; +using EngineV = + std::variant; class EngineService : public EngineServiceI { private: @@ -61,18 +64,21 @@ class EngineService : public EngineServiceI { }; HardwareInfo hw_inf_; std::shared_ptr db_service_ = nullptr; + std::shared_ptr q_ = nullptr; public: explicit EngineService( std::shared_ptr download_service, std::shared_ptr dylib_path_manager, - std::shared_ptr db_service) + std::shared_ptr db_service, + std::shared_ptr q) : download_service_{download_service}, dylib_path_manager_{dylib_path_manager}, hw_inf_{.sys_inf = system_info_utils::GetSystemInfo(), .cuda_driver_version = system_info_utils::GetDriverAndCudaVersion().second}, - db_service_(db_service) {} + db_service_(db_service), + q_(q) {} std::vector GetEngineInfoList() const; @@ -132,7 +138,6 @@ class EngineService : public EngineServiceI { cpp::result UpdateEngine( const std::string& engine); - cpp::result, std::string> GetEngines(); cpp::result GetEngineById(int id); @@ -157,6 +162,9 @@ class EngineService : public EngineServiceI { bool IsRemoteEngine(const std::string& engine_name) const override; + cpp::result, std::string> + GetEngineDirPath(const std::string& engine_name); + private: bool IsEngineLoaded(const std::string& engine); @@ -170,9 +178,6 @@ class EngineService : public EngineServiceI { std::string GetMatchedVariant(const std::string& engine, const std::vector& variants); - cpp::result, std::string> - GetEngineDirPath(const std::string& engine_name); - cpp::result IsEngineVariantReady( const std::string& engine, const std::string& version, const std::string& variant); diff --git a/engine/services/hardware_service.cc b/engine/services/hardware_service.cc index 7ee5fbfb3..dff5e9c7b 100644 --- a/engine/services/hardware_service.cc +++ b/engine/services/hardware_service.cc @@ -206,7 +206,10 @@ bool HardwareService::Restart(const std::string& host, int port) { auto download_srv = std::make_shared(); auto dylib_path_mng = std::make_shared(); auto db_srv = std::make_shared(); - EngineService(download_srv, dylib_path_mng, db_srv).RegisterEngineLibPath(); + // TODO(sang) refactor this + EngineService(download_srv, dylib_path_mng, db_srv, + std::make_shared(1, "task_queue")) + .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() / exe; commands.push_back(p); commands.push_back("--ignore_cout"); diff --git a/engine/services/inference_service.cc b/engine/services/inference_service.cc index 0a52665ad..78d788728 100644 --- a/engine/services/inference_service.cc +++ b/engine/services/inference_service.cc @@ -12,26 +12,29 @@ cpp::result InferenceService::HandleChatCompletion( } else { engine_type = (*(json_body)).get("engine", kLlamaRepo).asString(); } + CTL_DBG("engine_type: " << engine_type); function_calling_utils::PreprocessRequest(json_body); + CTL_DBG("engine_type: " << engine_type); auto tool_choice = json_body->get("tool_choice", Json::Value::null); auto model_id = json_body->get("model", "").asString(); - if (saved_models_.find(model_id) != saved_models_.end()) { - // check if model is started, if not start it first - Json::Value root; - root["model"] = model_id; - root["engine"] = engine_type; - auto ir = GetModelStatus(std::make_shared(root)); - auto status = std::get<0>(ir)["status_code"].asInt(); - if (status != drogon::k200OK) { - CTL_INF("Model is not loaded, start loading it: " << model_id); - // For remote engine, we use the updated configuration - if (engine_service_->IsRemoteEngine(engine_type)) { - (void)model_service_.lock()->StartModel(model_id, {}, false); - } else { - (void)LoadModel(saved_models_.at(model_id)); - } - } - } + // if (saved_models_.find(model_id) != saved_models_.end()) { + // // check if model is started, if not start it first + // Json::Value root; + // root["model"] = model_id; + // root["engine"] = engine_type; + // auto ir = GetModelStatus(std::make_shared(root)); + // auto status = std::get<0>(ir)["status_code"].asInt(); + // if (status != drogon::k200OK) { + // CTL_INF("Model is not loaded, start loading it: " << model_id); + // // For remote engine, we use the updated configuration + // if (engine_service_->IsRemoteEngine(engine_type)) { + // (void)model_service_.lock()->StartModel(model_id, {}, false); + // } else { + // (void)LoadModel(saved_models_.at(model_id)); + // } + // } + // } + CTL_DBG("engine_type: " << engine_type); auto engine_result = engine_service_->GetLoadedEngine(engine_type); if (engine_result.has_error()) { @@ -99,6 +102,9 @@ cpp::result InferenceService::HandleChatCompletion( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->HandleChatCompletion(json_body, std::move(cb)); + } else if (std::holds_alternative(engine_result.value())) { + std::get(engine_result.value()) + ->HandleChatCompletion(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->HandleChatCompletion(json_body, std::move(cb)); @@ -132,6 +138,9 @@ cpp::result InferenceService::HandleEmbedding( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->HandleEmbedding(json_body, std::move(cb)); + } else if (std::holds_alternative(engine_result.value())) { + std::get(engine_result.value()) + ->HandleEmbedding(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->HandleEmbedding(json_body, std::move(cb)); @@ -228,6 +237,9 @@ InferResult InferenceService::LoadModel( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->LoadModel(json_body, std::move(cb)); + } else if (std::holds_alternative(engine_result.value())) { + std::get(engine_result.value()) + ->LoadModel(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->LoadModel(json_body, std::move(cb)); @@ -264,6 +276,9 @@ InferResult InferenceService::UnloadModel(const std::string& engine_name, if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->UnloadModel(std::make_shared(json_body), std::move(cb)); + } else if (std::holds_alternative(engine_result.value())) { + std::get(engine_result.value()) + ->UnloadModel(std::make_shared(json_body), std::move(cb)); } else { std::get(engine_result.value()) ->UnloadModel(std::make_shared(json_body), std::move(cb)); @@ -302,6 +317,9 @@ InferResult InferenceService::GetModelStatus( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->GetModelStatus(json_body, std::move(cb)); + } else if (std::holds_alternative(engine_result.value())) { + std::get(engine_result.value()) + ->GetModelStatus(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->GetModelStatus(json_body, std::move(cb)); @@ -335,6 +353,9 @@ InferResult InferenceService::GetModels( if (e->IsSupported("GetModels")) { e->GetModels(json_body, std::move(cb)); } + } else if (std::holds_alternative(loaded_engine)) { + std::get(loaded_engine) + ->GetModels(json_body, std::move(cb)); } else { std::get(loaded_engine) ->GetModels(json_body, std::move(cb)); diff --git a/engine/utils/cuda_toolkit_utils.h b/engine/utils/cuda_toolkit_utils.h index 748af1bd3..e7aadfdd6 100644 --- a/engine/utils/cuda_toolkit_utils.h +++ b/engine/utils/cuda_toolkit_utils.h @@ -7,32 +7,7 @@ inline std::string GetCompatibleCudaToolkitVersion( const std::string& driver_semantic_version, const std::string& os, const std::string& engine) { - if (engine == "cortex.tensorrt-llm") { - // if the engine is cortex.tensorrt-llm, the minimum required CUDA version is 12.4 - if (os == "windows") { - if (semantic_version_utils::CompareSemanticVersion( - driver_semantic_version, "527.41") >= 0) { - return "12.4"; - } else { - throw std::runtime_error( - "GPU driver version not supported. Minimum " - "required driver version is 527.41"); - } - } else if (os == "linux") { - if (semantic_version_utils::CompareSemanticVersion( - driver_semantic_version, "525.60.13") >= 0) { - return "12.4"; - } else { - throw std::runtime_error( - "GPU driver version not supported. Minimum required driver version " - "is 525.60.13"); - } - } else { - throw std::runtime_error("Unsupported OS"); - } - } - - if (os == "windows") { + if (os == "windows" || os == "win") { if (semantic_version_utils::CompareSemanticVersion(driver_semantic_version, "527.41") >= 0) { return "12.4"; @@ -44,7 +19,7 @@ inline std::string GetCompatibleCudaToolkitVersion( "GPU driver version not supported. Minimum " "required driver version is 452.39"); } - } else if (os == "linux") { + } else if (os == "linux" || os == "ubuntu") { if (semantic_version_utils::CompareSemanticVersion(driver_semantic_version, "525.60.13") >= 0) { return "12.4"; diff --git a/engine/utils/engine_constants.h b/engine/utils/engine_constants.h index 35368c519..d865879be 100644 --- a/engine/utils/engine_constants.h +++ b/engine/utils/engine_constants.h @@ -11,7 +11,7 @@ constexpr const auto kRemote = "remote"; constexpr const auto kLocal = "local"; -constexpr const auto kLlamaRepo = "cortex.llamacpp"; +constexpr const auto kLlamaRepo = "llama.cpp"; constexpr const auto kPythonRuntimeRepo = "cortex.python"; constexpr const auto kLlamaLibPath = "./engines/cortex.llamacpp"; @@ -23,7 +23,7 @@ constexpr auto static kGitHubHost = "api.github.com"; constexpr auto static kCortexFolderName = "cortexcpp"; constexpr auto static kDefaultGHUserAgent = "cortexcpp"; -constexpr auto static kWindowsOs = "windows"; +constexpr auto static kWindowsOs = "win"; constexpr auto static kMacOs = "mac"; constexpr auto static kLinuxOs = "linux"; constexpr auto static kUnsupportedOs = "Unsupported OS"; diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index 0b0cb26be..7c99f4a0e 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -24,13 +24,20 @@ inline cpp::result GetVariantFromNameAndVersion( if (engine.empty()) { return cpp::fail("Engine name is empty"); } + CTL_INF("version: " << version); auto nv = string_utils::RemoveSubstring(version, "v"); + CTL_INF("nv: " << nv); using namespace string_utils; + CTL_INF("engine_file_name: " << engine_file_name); auto removed_extension = RemoveSubstring(engine_file_name, ".tar.gz"); - auto version_and_variant = RemoveSubstring(removed_extension, engine + "-"); - + auto removed_extension_e = RemoveSubstring(removed_extension, ".zip"); + CTL_INF("removed_extension: " << removed_extension_e); + auto version_and_variant = RemoveSubstring(removed_extension_e, engine + "-"); + CTL_INF("version_and_variant: " << version_and_variant); auto variant = RemoveSubstring(version_and_variant, nv + "-"); - return variant; + auto v = RemoveSubstring(variant, "llama-bin-"); + CTL_INF("variant: " << v); + return v; } inline std::string GetSuitableAvxVariant(cortex::cpuid::CpuInfo& cpu_info) { diff --git a/engine/utils/github_release_utils.h b/engine/utils/github_release_utils.h index 72d7687f6..8e8cb0393 100644 --- a/engine/utils/github_release_utils.h +++ b/engine/utils/github_release_utils.h @@ -179,9 +179,9 @@ inline cpp::result GetReleaseByVersion( if (tag != "latest") { path_params.push_back("tags"); - if (!string_utils::StartsWith(tag, "v")) { - path_params.push_back("v" + tag); - } + // if (!string_utils::StartsWith(tag, "v")) { + // path_params.push_back("v" + tag); + // } path_params.push_back(tag); } else { diff --git a/engine/utils/system_info_utils.h b/engine/utils/system_info_utils.h index 54eaed8c9..9bef6f4f9 100644 --- a/engine/utils/system_info_utils.h +++ b/engine/utils/system_info_utils.h @@ -70,7 +70,7 @@ inline std::unique_ptr GetSystemInfo() { #if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || \ defined(__amd64) || defined(__x86_64) || defined(_M_AMD64) - arch << "amd64"; + arch << "x64"; #elif defined(__arm__) || defined(__arm) || defined(__arm64__) || \ defined(__aarch64__) || defined(__thumb__) || \ defined(__TARGET_ARCH_ARM) || defined(__TARGET_ARCH_THUMB) || \ From 0968abef3288d697cb907821abee8540572e09d3 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Mon, 17 Mar 2025 13:06:06 +0700 Subject: [PATCH 02/49] chore: cleanup --- docs/docs/engines/engine-extension.mdx | 3 - engine/common/base.h | 3 - engine/controllers/server.cc | 11 --- engine/controllers/server.h | 7 -- engine/cortex-common/EngineI.h | 4 - engine/cortex-common/cortexpythoni.h | 22 ------ engine/cortex-common/local_enginei.h | 32 -------- engine/extensions/local-engine/local_engine.h | 25 +++++- .../extensions/python-engine/python_engine.cc | 13 ---- .../extensions/python-engine/python_engine.h | 2 - engine/services/engine_service.cc | 75 +----------------- engine/services/engine_service.h | 4 +- engine/services/inference_service.cc | 78 +------------------ engine/services/inference_service.h | 2 - 14 files changed, 30 insertions(+), 251 deletions(-) delete mode 100644 engine/cortex-common/cortexpythoni.h delete mode 100644 engine/cortex-common/local_enginei.h diff --git a/docs/docs/engines/engine-extension.mdx b/docs/docs/engines/engine-extension.mdx index 6bb966f60..21d57b5c5 100644 --- a/docs/docs/engines/engine-extension.mdx +++ b/docs/docs/engines/engine-extension.mdx @@ -71,9 +71,6 @@ class EngineI { std::shared_ptr json_body, std::function&& callback) = 0; - // Compatibility and model management - virtual bool IsSupported(const std::string& f) = 0; - virtual void GetModels( std::shared_ptr jsonBody, std::function&& callback) = 0; diff --git a/engine/common/base.h b/engine/common/base.h index b5de09059..fcaee860a 100644 --- a/engine/common/base.h +++ b/engine/common/base.h @@ -20,9 +20,6 @@ class BaseModel { virtual void GetModels( const HttpRequestPtr& req, std::function&& callback) = 0; - virtual void FineTuning( - const HttpRequestPtr& req, - std::function&& callback) = 0; }; class BaseChatCompletion { diff --git a/engine/controllers/server.cc b/engine/controllers/server.cc index a8cff2166..46d61c118 100644 --- a/engine/controllers/server.cc +++ b/engine/controllers/server.cc @@ -121,17 +121,6 @@ void server::GetModels(const HttpRequestPtr& req, LOG_TRACE << "Done get models"; } -void server::FineTuning( - const HttpRequestPtr& req, - std::function&& callback) { - auto ir = inference_svc_->FineTuning(req->getJsonObject()); - auto resp = cortex_utils::CreateCortexHttpJsonResponse(std::get<1>(ir)); - resp->setStatusCode( - static_cast(std::get<0>(ir)["status_code"].asInt())); - callback(resp); - LOG_TRACE << "Done fine-tuning"; -} - void server::Inference(const HttpRequestPtr& req, std::function&& callback) { diff --git a/engine/controllers/server.h b/engine/controllers/server.h index 42214a641..246eaa79b 100644 --- a/engine/controllers/server.h +++ b/engine/controllers/server.h @@ -39,12 +39,8 @@ class server : public drogon::HttpController, METHOD_ADD(server::ModelStatus, "modelstatus", Options, Post); METHOD_ADD(server::GetModels, "models", Get); - // cortex.python API - METHOD_ADD(server::FineTuning, "finetuning", Options, Post); - // Openai compatible path ADD_METHOD_TO(server::ChatCompletion, "/v1/chat/completions", Options, Post); - ADD_METHOD_TO(server::FineTuning, "/v1/fine_tuning/job", Options, Post); ADD_METHOD_TO(server::Embedding, "/v1/embeddings", Options, Post); ADD_METHOD_TO(server::Inference, "/v1/inference", Options, Post); ADD_METHOD_TO(server::RouteRequest, "/v1/route/request", Options, Post); @@ -69,9 +65,6 @@ class server : public drogon::HttpController, void GetModels( const HttpRequestPtr& req, std::function&& callback) override; - void FineTuning( - const HttpRequestPtr& req, - std::function&& callback) override; void Inference(const HttpRequestPtr& req, std::function&& callback); void RouteRequest(const HttpRequestPtr& req, diff --git a/engine/cortex-common/EngineI.h b/engine/cortex-common/EngineI.h index 754f16593..eb46726a7 100644 --- a/engine/cortex-common/EngineI.h +++ b/engine/cortex-common/EngineI.h @@ -47,9 +47,6 @@ class EngineI { std::shared_ptr json_body, std::function&& callback) = 0; - // For backward compatible checking - virtual bool IsSupported(const std::string& f) = 0; - // Get list of running models virtual void GetModels( std::shared_ptr jsonBody, @@ -62,7 +59,6 @@ class EngineI { // Stop inflight chat completion in stream mode virtual void StopInferencing(const std::string& model_id) = 0; - virtual Json::Value GetRemoteModels() = 0; virtual void HandleRouteRequest( std::shared_ptr json_body, std::function&& callback) = 0; diff --git a/engine/cortex-common/cortexpythoni.h b/engine/cortex-common/cortexpythoni.h deleted file mode 100644 index 06a79838f..000000000 --- a/engine/cortex-common/cortexpythoni.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -#include "json/value.h" - -class CortexPythonEngineI { - public: - virtual ~CortexPythonEngineI() {} - - virtual bool IsSupported(const std::string& f) = 0; - - virtual void ExecutePythonFile(std::string binary_execute_path, - std::string file_execution_path, - std::string python_library_path) = 0; - - virtual void HandlePythonFileExecutionRequest( - std::shared_ptr json_body, - std::function&& callback) = 0; -}; - diff --git a/engine/cortex-common/local_enginei.h b/engine/cortex-common/local_enginei.h deleted file mode 100644 index 18d2a1727..000000000 --- a/engine/cortex-common/local_enginei.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -#include "json/value.h" -#include "trantor/utils/Logger.h" -class LocalEngineI { - public: - virtual ~LocalEngineI() {} - - virtual void HandleChatCompletion( - std::shared_ptr json_body, - std::function&& callback) = 0; - virtual void HandleEmbedding( - std::shared_ptr json_body, - std::function&& callback) = 0; - virtual void LoadModel( - std::shared_ptr json_body, - std::function&& callback) = 0; - virtual void UnloadModel( - std::shared_ptr json_body, - std::function&& callback) = 0; - virtual void GetModelStatus( - std::shared_ptr json_body, - std::function&& callback) = 0; - - // Get list of running models - virtual void GetModels( - std::shared_ptr jsonBody, - std::function&& callback) = 0; -}; \ No newline at end of file diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h index b4455f397..e344541b9 100644 --- a/engine/extensions/local-engine/local_engine.h +++ b/engine/extensions/local-engine/local_engine.h @@ -4,7 +4,7 @@ #include #include #include -#include "cortex-common/local_enginei.h" +#include "cortex-common/EngineI.h" #include "json/json.h" #include "services/engine_service.h" #include "utils/process/utils.h" @@ -16,11 +16,16 @@ struct ServerAddress { int port; cortex::process::ProcessInfo process_info; }; -class LocalEngine : public LocalEngineI { +class LocalEngine : public EngineI { public: LocalEngine(EngineService& engine_service, TaskQueue& q) : engine_service_(engine_service), q_(q) {} ~LocalEngine(); + + void Load(EngineLoadOption opts) final {} + + void Unload(EngineUnloadOption opts) final {} + void HandleChatCompletion( std::shared_ptr json_body, std::function&& callback) final; @@ -42,6 +47,22 @@ class LocalEngine : public LocalEngineI { std::shared_ptr jsonBody, std::function&& callback) final; + bool SetFileLogger(int max_log_lines, const std::string& log_path) final { + return true; + } + void SetLogLevel(trantor::Logger::LogLevel logLevel) final {} + + // Stop inflight chat completion in stream mode + void StopInferencing(const std::string& model_id) final {} + + void HandleRouteRequest( + std::shared_ptr json_body, + std::function&& callback) final {} + + void HandleInference( + std::shared_ptr json_body, + std::function&& callback) final {} + private: std::unordered_map server_map_; EngineService& engine_service_; diff --git a/engine/extensions/python-engine/python_engine.cc b/engine/extensions/python-engine/python_engine.cc index 31a667b5c..55efd76d3 100644 --- a/engine/extensions/python-engine/python_engine.cc +++ b/engine/extensions/python-engine/python_engine.cc @@ -622,10 +622,6 @@ void PythonEngine::HandleInference( } } -Json::Value PythonEngine::GetRemoteModels() { - return Json::Value(); -} - void PythonEngine::StopInferencing(const std::string& model_id) { (void)model_id; } @@ -860,15 +856,6 @@ void PythonEngine::HandleEmbedding( callback(Json::Value(), Json::Value()); } -bool PythonEngine::IsSupported(const std::string& f) { - if (f == "HandleChatCompletion" || f == "LoadModel" || f == "UnloadModel" || - f == "GetModelStatus" || f == "GetModels" || f == "SetFileLogger" || - f == "SetLogLevel") { - return true; - } - return false; -} - bool PythonEngine::SetFileLogger(int max_log_lines, const std::string& log_path) { if (!async_file_logger_) { diff --git a/engine/extensions/python-engine/python_engine.h b/engine/extensions/python-engine/python_engine.h index 2c2883809..7954110a1 100644 --- a/engine/extensions/python-engine/python_engine.h +++ b/engine/extensions/python-engine/python_engine.h @@ -95,7 +95,6 @@ class PythonEngine : public EngineI { void HandleEmbedding( std::shared_ptr json_body, std::function&& callback) override; - bool IsSupported(const std::string& feature) override; bool SetFileLogger(int max_log_lines, const std::string& log_path) override; void SetLogLevel(trantor::Logger::LogLevel logLevel) override; void HandleRouteRequest( @@ -104,7 +103,6 @@ class PythonEngine : public EngineI { void HandleInference( std::shared_ptr json_body, std::function&& callback) override; - Json::Value GetRemoteModels() override; void StopInferencing(const std::string& model_id) override; }; } // namespace python_engine \ No newline at end of file diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index 7c37587f0..ea7fe9322 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -770,6 +770,9 @@ cpp::result EngineService::LoadEngine( } if (engines_.find(ne) == engines_.end()) { CTL_INF("Loading local engine: " << engine_name); +#if defined(_WIN32) || defined(_WIN64) || defined(__linux__) + CTL_INF("CPU Info: " << cortex::cpuid::CpuInfo().to_string()); +#endif engines_[ne].engine = new cortex::local::LocalEngine(*this, *(q_.get())); CTL_INF("Loaded engine: " << engine_name); } else { @@ -777,76 +780,6 @@ cpp::result EngineService::LoadEngine( } return {}; - - // End hard code - - CTL_INF("Loading engine: " << ne); -#if defined(_WIN32) || defined(_WIN64) || defined(__linux__) - CTL_INF("CPU Info: " << cortex::cpuid::CpuInfo().to_string()); -#endif - - auto engine_dir_path_res = GetEngineDirPath(ne); - if (engine_dir_path_res.has_error()) { - return cpp::fail(engine_dir_path_res.error()); - } - auto engine_dir_path = engine_dir_path_res.value().first; - auto custom_engine_path = engine_dir_path_res.value().second; - - try { - auto cuda_path = file_manager_utils::GetCudaToolkitPath(ne); - -#if defined(_WIN32) || defined(_WIN64) - // register deps - if (!(getenv("ENGINE_PATH"))) { - std::vector paths{}; - paths.push_back(cuda_path); - paths.push_back(engine_dir_path); - - CTL_DBG("Registering dylib for " - << ne << " with " << std::to_string(paths.size()) << " paths."); - for (const auto& path : paths) { - CTL_DBG("Registering path: " << path.string()); - } - - auto reg_result = dylib_path_manager_->RegisterPath(ne, paths); - if (reg_result.has_error()) { - CTL_DBG("Failed register lib paths for: " << ne); - } else { - CTL_DBG("Registered lib paths for: " << ne); - } - } -#endif - - auto dylib = - std::make_unique(engine_dir_path.string(), "engine"); - - auto config = file_manager_utils::GetCortexConfig(); - auto log_path = std::filesystem::path(config.logFolderPath) / - std::filesystem::path(config.logLlamaCppPath); - - // init - auto func = dylib->get_function("get_engine"); - auto engine_obj = func(); - auto load_opts = EngineI::EngineLoadOption{ - .engine_path = engine_dir_path, - .deps_path = cuda_path, - .is_custom_engine_path = custom_engine_path, - .log_path = log_path, - .max_log_lines = config.maxLogLines, - .log_level = logging_utils_helper::global_log_level, - }; - engine_obj->Load(load_opts); - - engines_[ne].engine = engine_obj; - engines_[ne].dl = std::move(dylib); - - CTL_DBG("Engine loaded: " << ne); - return {}; - } catch (const cortex_cpp::dylib::load_error& e) { - CTL_ERR("Could not load engine: " << e.what()); - engines_.erase(ne); - return cpp::fail("Could not load engine " + ne + ": " + e.what()); - } } void EngineService::RegisterEngineLibPath() { @@ -952,8 +885,6 @@ cpp::result EngineService::UnloadEngine( auto unload_opts = EngineI::EngineUnloadOption{}; e->Unload(unload_opts); delete e; - } else if (std::holds_alternative(engines_[ne].engine)) { - delete std::get(engines_[ne].engine); } else { delete std::get(engines_[ne].engine); } diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index d94bb9de5..7fc1b4d58 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -9,8 +9,6 @@ #include "common/engine_servicei.h" #include "cortex-common/EngineI.h" -#include "cortex-common/cortexpythoni.h" -#include "cortex-common/local_enginei.h" #include "cortex-common/remote_enginei.h" #include "database/engines.h" #include "services/database_service.h" @@ -40,7 +38,7 @@ struct EngineUpdateResult { }; using EngineV = - std::variant; + std::variant; class EngineService : public EngineServiceI { private: diff --git a/engine/services/inference_service.cc b/engine/services/inference_service.cc index 78d788728..b05af1be0 100644 --- a/engine/services/inference_service.cc +++ b/engine/services/inference_service.cc @@ -102,9 +102,6 @@ cpp::result InferenceService::HandleChatCompletion( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->HandleChatCompletion(json_body, std::move(cb)); - } else if (std::holds_alternative(engine_result.value())) { - std::get(engine_result.value()) - ->HandleChatCompletion(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->HandleChatCompletion(json_body, std::move(cb)); @@ -138,9 +135,6 @@ cpp::result InferenceService::HandleEmbedding( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->HandleEmbedding(json_body, std::move(cb)); - } else if (std::holds_alternative(engine_result.value())) { - std::get(engine_result.value()) - ->HandleEmbedding(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->HandleEmbedding(json_body, std::move(cb)); @@ -237,9 +231,6 @@ InferResult InferenceService::LoadModel( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->LoadModel(json_body, std::move(cb)); - } else if (std::holds_alternative(engine_result.value())) { - std::get(engine_result.value()) - ->LoadModel(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->LoadModel(json_body, std::move(cb)); @@ -276,9 +267,6 @@ InferResult InferenceService::UnloadModel(const std::string& engine_name, if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->UnloadModel(std::make_shared(json_body), std::move(cb)); - } else if (std::holds_alternative(engine_result.value())) { - std::get(engine_result.value()) - ->UnloadModel(std::make_shared(json_body), std::move(cb)); } else { std::get(engine_result.value()) ->UnloadModel(std::make_shared(json_body), std::move(cb)); @@ -317,9 +305,6 @@ InferResult InferenceService::GetModelStatus( if (std::holds_alternative(engine_result.value())) { std::get(engine_result.value()) ->GetModelStatus(json_body, std::move(cb)); - } else if (std::holds_alternative(engine_result.value())) { - std::get(engine_result.value()) - ->GetModelStatus(json_body, std::move(cb)); } else { std::get(engine_result.value()) ->GetModelStatus(json_body, std::move(cb)); @@ -350,12 +335,7 @@ InferResult InferenceService::GetModels( for (const auto& loaded_engine : loaded_engines) { if (std::holds_alternative(loaded_engine)) { auto e = std::get(loaded_engine); - if (e->IsSupported("GetModels")) { - e->GetModels(json_body, std::move(cb)); - } - } else if (std::holds_alternative(loaded_engine)) { - std::get(loaded_engine) - ->GetModels(json_body, std::move(cb)); + e->GetModels(json_body, std::move(cb)); } else { std::get(loaded_engine) ->GetModels(json_body, std::move(cb)); @@ -369,56 +349,6 @@ InferResult InferenceService::GetModels( return std::make_pair(stt, root); } -InferResult InferenceService::FineTuning( - std::shared_ptr json_body) { - std::string ne = kPythonRuntimeRepo; - Json::Value r; - Json::Value stt; - - // TODO: namh refactor this - // if (engines_.find(ne) == engines_.end()) { - // try { - // std::string abs_path = - // (getenv("ENGINE_PATH") - // ? getenv("ENGINE_PATH") - // : file_manager_utils::GetCortexDataPath().string()) + - // kPythonRuntimeLibPath; - // engines_[ne].dl = std::make_unique(abs_path, "engine"); - // } catch (const cortex_cpp::dylib::load_error& e) { - // - // LOG_ERROR << "Could not load engine: " << e.what(); - // engines_.erase(ne); - // - // Json::Value res; - // r["message"] = "Could not load engine " + ne; - // stt["status_code"] = drogon::k500InternalServerError; - // return std::make_pair(stt, r); - // } - // - // auto func = - // engines_[ne].dl->get_function("get_engine"); - // engines_[ne].engine = func(); - // LOG_INFO << "Loaded engine: " << ne; - // } - // - // LOG_TRACE << "Start to fine-tuning"; - // auto& en = std::get(engines_[ne].engine); - // if (en->IsSupported("HandlePythonFileExecutionRequest")) { - // en->HandlePythonFileExecutionRequest( - // json_body, [&r, &stt](Json::Value status, Json::Value res) { - // r = res; - // stt = status; - // }); - // } else { - // LOG_WARN << "Method is not supported yet"; - r["message"] = "Method is not supported yet"; - stt["status_code"] = drogon::k500InternalServerError; - // return std::make_pair(stt, r); - // } - // LOG_TRACE << "Done fine-tuning"; - return std::make_pair(stt, r); -} - bool InferenceService::StopInferencing(const std::string& engine_name, const std::string& model_id) { CTL_DBG("Stop inferencing"); @@ -430,10 +360,8 @@ bool InferenceService::StopInferencing(const std::string& engine_name, if (std::holds_alternative(engine_result.value())) { auto engine = std::get(engine_result.value()); - if (engine->IsSupported("StopInferencing")) { - engine->StopInferencing(model_id); - CTL_INF("Stopped inferencing"); - } + engine->StopInferencing(model_id); + CTL_INF("Stopped inferencing"); } return true; } diff --git a/engine/services/inference_service.h b/engine/services/inference_service.h index 726275bba..b73a88c5d 100644 --- a/engine/services/inference_service.h +++ b/engine/services/inference_service.h @@ -57,8 +57,6 @@ class InferenceService { InferResult GetModels(std::shared_ptr json_body); - InferResult FineTuning(std::shared_ptr json_body); - bool StopInferencing(const std::string& engine_name, const std::string& model_id); From 219d460e6f15b0ca078462b452383b868de0ebfc Mon Sep 17 00:00:00 2001 From: sangjanai Date: Tue, 18 Mar 2025 14:12:45 +0700 Subject: [PATCH 03/49] feat: OAI --- .../extensions/local-engine/local_engine.cc | 754 +++++++++++++++--- engine/extensions/local-engine/local_engine.h | 55 +- engine/utils/string_utils.h | 6 + 3 files changed, 679 insertions(+), 136 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 1898b084e..c56fb6192 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -96,11 +96,156 @@ std::vector ConvertJsonToParamsVector(const Json::Value& root) { constexpr const auto kMinDataChunkSize = 6u; +struct OaiInfo { + std::string model; + bool include_usage = false; + bool oai_endpoint = false; + int n_probs = 0; +}; + struct StreamingCallback { - std::shared_ptr> callback; + std::shared_ptr callback; bool need_stop = true; + OaiInfo oi; +}; + +struct Usage { + int prompt_tokens = 0; + int completion_tokens = 0; }; +std::string GenerateRandomString(std::size_t length) { + const std::string characters = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + std::random_device rd; + std::mt19937 generator(rd()); + + std::uniform_int_distribution<> distribution( + 0, static_cast(characters.size()) - 1); + + std::string random_string(length, '\0'); + std::generate_n(random_string.begin(), length, + [&]() { return characters[distribution(generator)]; }); + + return random_string; +} + +std::vector GetUTF8Bytes(const std::string& str) { + std::vector bytes; + for (unsigned char c : str) { + bytes.push_back(static_cast(c)); + } + return bytes; +} + +Json::Value TransformLogProbs(const Json::Value& logprobs) { + Json::Value root; + Json::Value logprobs_json(Json::arrayValue); + + // Iterate through each token group in the input + for (const auto& token_group : logprobs) { + Json::Value content_item; + + // Set the token (content) + content_item["token"] = token_group["content"].asString(); + + // Get the probabilities array + const auto& probs = token_group["probs"]; + + // Set the main token's logprob (first probability) + if (!probs.empty()) { + content_item["logprob"] = std::log( + probs[0]["prob"].asDouble() + std::numeric_limits::epsilon()); + } + + // Get UTF-8 bytes for the token + auto bytes = GetUTF8Bytes(token_group["content"].asString()); + Json::Value bytes_array(Json::arrayValue); + for (int byte : bytes) { + bytes_array.append(byte); + } + content_item["bytes"] = bytes_array; + + // Create top_logprobs array + Json::Value top_logprobs(Json::arrayValue); + for (const auto& prob_item : probs) { + Json::Value logprob_item; + logprob_item["token"] = prob_item["tok_str"].asString(); + logprob_item["logprob"] = + std::log(prob_item["prob"].asDouble() + + std::numeric_limits::epsilon()); + + // Get UTF-8 bytes for this alternative token + auto alt_bytes = GetUTF8Bytes(prob_item["tok_str"].asString()); + Json::Value alt_bytes_array(Json::arrayValue); + for (int byte : alt_bytes) { + alt_bytes_array.append(byte); + } + logprob_item["bytes"] = alt_bytes_array; + + top_logprobs.append(logprob_item); + } + content_item["top_logprobs"] = top_logprobs; + + logprobs_json.append(content_item); + } + root["content"] = logprobs_json; + return root; +} + +std::string CreateReturnJson( + const std::string& id, const std::string& model, const std::string& content, + Json::Value finish_reason, bool include_usage, + std::optional usage = std::nullopt, + std::optional logprobs = std::nullopt) { + Json::Value root; + + root["id"] = id; + root["model"] = model; + root["created"] = static_cast(std::time(nullptr)); + root["object"] = "chat.completion.chunk"; + + Json::Value choicesArray(Json::arrayValue); + // If usage, the choices field will always be an empty array + if (!usage) { + Json::Value choice; + + choice["index"] = 0; + Json::Value delta; + delta["content"] = content; + delta["role"] = "assistant"; + choice["delta"] = delta; + choice["finish_reason"] = finish_reason; + if (logprobs.has_value() && !logprobs.value().empty()) { + choice["logprobs"] = TransformLogProbs(logprobs.value()); + } + + choicesArray.append(choice); + } + root["choices"] = choicesArray; + if (include_usage) { + if (usage) { + Json::Value usage_json; + Json::Value details; + details["reasoning_tokens"] = 0; + usage_json["prompt_tokens"] = (*usage).prompt_tokens; + usage_json["completion_tokens"] = (*usage).completion_tokens; + usage_json["total_tokens"] = + (*usage).prompt_tokens + (*usage).completion_tokens; + usage_json["completion_tokens_details"] = details; + root["usage"] = usage_json; + } else { + root["usage"] = Json::Value(); + } + } + + Json::StreamWriterBuilder writer; + writer["indentation"] = ""; // This sets the indentation to an empty string, + // producing compact output. + return Json::writeString(writer, root); +} + size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { auto* sc = static_cast(userdata); size_t data_length = size * nmemb; @@ -108,128 +253,178 @@ size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { if (ptr && data_length > kMinDataChunkSize) { std::string chunk(ptr + kMinDataChunkSize, data_length - kMinDataChunkSize); CTL_DBG(chunk); - if (chunk.find("[DONE]") != std::string::npos) { + if (sc->oi.oai_endpoint) { + if (chunk.find("[DONE]") != std::string::npos) { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + Json::Value chunk_json; + chunk_json["data"] = "data: [DONE]"; + sc->need_stop = false; + (*sc->callback)(std::move(status), std::move(chunk_json)); + return data_length; + } + if (!sc->oi.include_usage && + chunk.find("completion_tokens") != std::string::npos) { + return data_length; + } + + Json::Value chunk_json; + chunk_json["data"] = "data: " + chunk; Json::Value status; - status["is_done"] = true; + status["is_done"] = false; status["has_error"] = false; status["is_stream"] = true; status["status_code"] = 200; + (*sc->callback)(std::move(status), std::move(chunk_json)); + } else { + if (chunk.find("[DONE]") != std::string::npos) { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + Json::Value chunk_json; + chunk_json["data"] = "data: [DONE]"; + sc->need_stop = false; + (*sc->callback)(std::move(status), std::move(chunk_json)); + return data_length; + } + auto json_data = json_helper::ParseJsonString(chunk); + // DONE + if (!json_data.isNull() && json_data.isMember("timings")) { + std::optional u; + if (sc->oi.include_usage) { + u = Usage{json_data["tokens_evaluated"].asInt(), + json_data["tokens_predicted"].asInt()}; + } + + Json::Value chunk_json; + chunk_json["data"] = + "data: " + CreateReturnJson(GenerateRandomString(20), sc->oi.model, + "", "stop", sc->oi.include_usage, u); + Json::Value status; + status["is_done"] = false; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + (*sc->callback)(std::move(status), std::move(chunk_json)); + + sc->need_stop = false; + return data_length; + } + + Json::Value logprobs; + if (sc->oi.n_probs > 0) { + logprobs = json_data["completion_probabilities"]; + } + std::string to_send; + if (json_data.isMember("choices") && json_data["choices"].isArray() && + json_data["choices"].size() > 0) { + to_send = json_data["choices"][0].get("text", "").asString(); + } + CTL_DBG(to_send); + const std::string str = + CreateReturnJson(GenerateRandomString(20), sc->oi.model, to_send, "", + sc->oi.include_usage, std::nullopt, logprobs); Json::Value chunk_json; - chunk_json["data"] = "data: [DONE]"; - sc->need_stop = false; + chunk_json["data"] = "data: " + str; + Json::Value status; + status["is_done"] = false; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; (*sc->callback)(std::move(status), std::move(chunk_json)); return data_length; } - Json::Value chunk_json; - chunk_json["data"] = "data: " + chunk; - Json::Value status; - status["is_done"] = false; - status["has_error"] = false; - status["is_stream"] = true; - status["status_code"] = 200; - (*sc->callback)(std::move(status), std::move(chunk_json)); } return data_length; } + +Json::Value ConvertLogitBiasToArray(const Json::Value& input) { + Json::Value result(Json::arrayValue); + if (input.isObject()) { + const auto& member_names = input.getMemberNames(); + for (const auto& tokenStr : member_names) { + Json::Value pair(Json::arrayValue); + pair.append(std::stoi(tokenStr)); + pair.append(input[tokenStr].asFloat()); + result.append(pair); + } + } + return result; +} + +Json::Value CreateFullReturnJson( + const std::string& id, const std::string& model, const std::string& content, + const std::string& system_fingerprint, int prompt_tokens, + int completion_tokens, Json::Value finish_reason = Json::Value(), + std::optional logprobs = std::nullopt) { + Json::Value root; + + root["id"] = id; + root["model"] = model; + root["created"] = static_cast(std::time(nullptr)); + root["object"] = "chat.completion"; + root["system_fingerprint"] = system_fingerprint; + + Json::Value choicesArray(Json::arrayValue); + Json::Value choice; + + choice["index"] = 0; + Json::Value message; + message["role"] = "assistant"; + message["content"] = content; + choice["message"] = message; + choice["finish_reason"] = finish_reason; + if (logprobs.has_value() && !logprobs.value().empty()) { + choice["logprobs"] = TransformLogProbs(logprobs.value()); + } + + choicesArray.append(choice); + root["choices"] = choicesArray; + + Json::Value usage; + usage["prompt_tokens"] = prompt_tokens; + usage["completion_tokens"] = completion_tokens; + usage["total_tokens"] = prompt_tokens + completion_tokens; + root["usage"] = usage; + + return root; +} + } // namespace LocalEngine::~LocalEngine() { for (auto& [_, si] : server_map_) { - (void) cortex::process::KillProcess(si.process_info); + (void)cortex::process::KillProcess(si.process_info); } server_map_.clear(); } -void LocalEngine::HandleChatCompletion( - std::shared_ptr json_body, - std::function&& callback) { +void LocalEngine::HandleChatCompletion(std::shared_ptr json_body, + http_callback&& callback) { auto model_id = json_body->get("model", "").asString(); if (model_id.empty()) { CTL_WRN("Model is empty"); } if (server_map_.find(model_id) != server_map_.end()) { auto& s = server_map_[model_id]; - bool is_stream = json_body->get("stream", false).asBool(); - if (is_stream) { - q_.RunInQueue([s, json_body, callback] { - auto curl = curl_easy_init(); - if (!curl) { - CTL_WRN("Failed to initialize CURL"); - return; - } - - auto url = "http://" + s.host + ":" + std::to_string(s.port) + - "/v1/chat/completions"; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_POST, 1L); - - struct curl_slist* headers = nullptr; - headers = curl_slist_append(headers, "Content-Type: application/json"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - auto json_str = json_body->toStyledString(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_str.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json_str.length()); - curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); - - StreamingCallback sc; - sc.callback = - std::make_shared>( - callback); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sc); - auto res = curl_easy_perform(curl); - - if (res != CURLE_OK) { - CTL_WRN("CURL request failed: " << curl_easy_strerror(res)); - - Json::Value status; - status["is_done"] = true; - status["has_error"] = true; - status["is_stream"] = true; - status["status_code"] = 500; - - Json::Value error; - error["error"] = curl_easy_strerror(res); - callback(std::move(status), std::move(error)); - } - curl_easy_cleanup(curl); - if (sc.need_stop) { - CTL_DBG("No stop message received, need to stop"); - Json::Value status; - status["is_done"] = true; - status["has_error"] = false; - status["is_stream"] = true; - status["status_code"] = 200; - (*sc.callback)(std::move(status), Json::Value()); - } - }); - - } else { - auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "chat", - "completions", - }, - }; - - auto response = curl_utils::SimplePostJson(url.ToFullPath(), - json_body->toStyledString()); - - if (response.has_error()) { - CTL_WRN("Error: " << response.error()); - } else { - Json::Value status; - status["is_done"] = true; - status["has_error"] = false; - status["is_stream"] = false; - status["status_code"] = 200; - callback(std::move(status), std::move(response.value())); + auto oaicompat = [&json_body]() -> bool { + if (json_body->isMember("logprobs") && + (*json_body)["logprobs"].asBool()) { + return false; } + return true; + }(); + if (oaicompat) { + HandleOpenAiChatCompletion( + json_body, const_cast(callback), model_id); + } else { + HandleNonOpenAiChatCompletion( + json_body, const_cast(callback), model_id); } } else { Json::Value error; @@ -243,9 +438,8 @@ void LocalEngine::HandleChatCompletion( } } -void LocalEngine::HandleEmbedding( - std::shared_ptr json_body, - std::function&& callback) { +void LocalEngine::HandleEmbedding(std::shared_ptr json_body, + http_callback&& callback) { auto model_id = json_body->get("model", "").asString(); if (model_id.empty()) { CTL_WRN("Model is empty"); @@ -295,9 +489,8 @@ void LocalEngine::HandleEmbedding( } } -void LocalEngine::LoadModel( - std::shared_ptr json_body, - std::function&& callback) { +void LocalEngine::LoadModel(std::shared_ptr json_body, + http_callback&& callback) { CTL_INF("Start loading model"); auto wait_for_server_up = [](const std::string& host, int port) { auto url = url_parser::Url{ @@ -390,9 +583,8 @@ void LocalEngine::LoadModel( } } -void LocalEngine::UnloadModel( - std::shared_ptr json_body, - std::function&& callback) { +void LocalEngine::UnloadModel(std::shared_ptr json_body, + http_callback&& callback) { auto model_id = json_body->get("model", "").asString(); if (model_id.empty()) { CTL_WRN("Model is empty"); @@ -440,9 +632,8 @@ void LocalEngine::UnloadModel( } } -void LocalEngine::GetModelStatus( - std::shared_ptr json_body, - std::function&& callback) { +void LocalEngine::GetModelStatus(std::shared_ptr json_body, + http_callback&& callback) { auto model_id = json_body->get("model", "").asString(); if (model_id.empty()) { CTL_WRN("Model is empty"); @@ -468,7 +659,346 @@ void LocalEngine::GetModelStatus( } } -void LocalEngine::GetModels( - std::shared_ptr json_body, - std::function&& callback) {} +void LocalEngine::GetModels(std::shared_ptr json_body, + http_callback&& callback) {} + +void LocalEngine::HandleOpenAiChatCompletion( + std::shared_ptr json_body, http_callback&& callback, + const std::string& model) { + CTL_DBG("Hanle OpenAI chat completion"); + auto is_stream = (*json_body).get("stream", false).asBool(); + auto include_usage = [&json_body, is_stream]() -> bool { + if (is_stream) { + if (json_body->isMember("stream_options") && + !(*json_body)["stream_options"].isNull()) { + return (*json_body)["stream_options"] + .get("include_usage", false) + .asBool(); + } + return false; + } + return false; + }(); + + auto n = [&json_body, is_stream]() -> int { + if (is_stream) + return 1; + return (*json_body).get("n", 1).asInt(); + }(); + + auto& s = server_map_.at(model); + // Format logit_bias + if (json_body->isMember("logit_bias")) { + auto logit_bias = ConvertLogitBiasToArray((*json_body)["logit_bias"]); + (*json_body)["logit_bias"] = logit_bias; + } + // llama.cpp server only supports n = 1 + (*json_body)["n"] = 1; + + if (is_stream) { + q_.RunInQueue([s, json_body, callback, model] { + auto curl = curl_easy_init(); + if (!curl) { + CTL_WRN("Failed to initialize CURL"); + return; + } + + auto url = "http://" + s.host + ":" + std::to_string(s.port) + + "/v1/chat/completions"; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + auto json_str = json_body->toStyledString(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_str.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json_str.length()); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + + StreamingCallback sc; + OaiInfo oi{model, false /*include_usage*/, true /*oai_endpoint*/, + 0 /*n_probs*/}; + sc.callback = std::make_shared(callback); + sc.need_stop = true; + sc.oi = oi; + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sc); + auto res = curl_easy_perform(curl); + + if (res != CURLE_OK) { + CTL_WRN("CURL request failed: " << curl_easy_strerror(res)); + + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = true; + status["status_code"] = 500; + + Json::Value error; + error["error"] = curl_easy_strerror(res); + callback(std::move(status), std::move(error)); + } + curl_easy_cleanup(curl); + if (sc.need_stop) { + CTL_DBG("No stop message received, need to stop"); + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + (*sc.callback)(std::move(status), Json::Value()); + } + }); + + } else { + auto url = url_parser::Url{ + .protocol = "http", + .host = s.host + ":" + std::to_string(s.port), + .pathParams = + { + "v1", + "chat", + "completions", + }, + }; + Json::Value result; + // multiple choices + for (int i = 0; i < n; i++) { + auto response = curl_utils::SimplePostJson(url.ToFullPath(), + json_body->toStyledString()); + + if (response.has_value()) { + auto r = response.value(); + if (i == 0) { + result = r; + } else { + r["choices"][0]["index"] = i; + result["choices"].append(r["choices"][0]); + result["usage"]["completion_tokens"] = + result["usage"]["completion_tokens"].asInt() + + r["usage"]["completion_tokens"].asInt(); + result["usage"]["prompt_tokens"] = + result["usage"]["prompt_tokens"].asInt() + + r["usage"]["prompt_tokens"].asInt(); + result["usage"]["total_tokens"] = + result["usage"]["total_tokens"].asInt() + + r["usage"]["total_tokens"].asInt(); + } + + if (i == n - 1) { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(result)); + } + } else { + CTL_WRN("Error: " << response.error()); + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 500; + callback(std::move(status), std::move(response.value())); + break; + } + } + } +} + +void LocalEngine::HandleNonOpenAiChatCompletion( + std::shared_ptr json_body, http_callback&& callback, + const std::string& model) { + CTL_DBG("Hanle NonOpenAI chat completion"); + auto is_stream = (*json_body).get("stream", false).asBool(); + auto include_usage = [&json_body, is_stream]() -> bool { + if (is_stream) { + if (json_body->isMember("stream_options") && + !(*json_body)["stream_options"].isNull()) { + return (*json_body)["stream_options"] + .get("include_usage", false) + .asBool(); + } + return false; + } + return false; + }(); + + auto n = [&json_body, is_stream]() -> int { + if (is_stream) + return 1; + return (*json_body).get("n", 1).asInt(); + }(); + + auto& s = server_map_.at(model); + + // Format logit_bias + if (json_body->isMember("logit_bias")) { + auto logit_bias = ConvertLogitBiasToArray((*json_body)["logit_bias"]); + (*json_body)["logit_bias"] = logit_bias; + } + auto get_message = [](const Json::Value& msg_content) -> std::string { + if (msg_content.isArray()) { + for (const auto& mc : msg_content) { + if (mc["type"].asString() == "text") { + return mc["text"].asString(); + } + } + } else { + return msg_content.asString(); + } + return ""; + }; + + if (!json_body->isMember("prompt") || + (*json_body)["prompt"].asString().empty()) { + auto formatted_output = s.pre_prompt; + for (const auto& message : (*json_body)["messages"]) { + auto input_role = message["role"].asString(); + std::string role; + if (input_role == "user") { + role = s.user_prompt; + } else if (input_role == "assistant") { + role = s.ai_prompt; + } else if (input_role == "system") { + role = s.system_prompt; + } else { + role = input_role; + } + + if (auto content = get_message(message["content"]); !content.empty()) { + formatted_output += role + content; + } + } + formatted_output += s.ai_prompt; + (*json_body)["prompt"] = formatted_output; + } + + (*json_body)["n"] = 1; + int n_probs = json_body->get("n_probs", 0).asInt(); + + if (is_stream) { + q_.RunInQueue([s, json_body, callback, n_probs, model] { + auto curl = curl_easy_init(); + if (!curl) { + CTL_WRN("Failed to initialize CURL"); + return; + } + + auto url = + "http://" + s.host + ":" + std::to_string(s.port) + "/v1/completions"; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + auto json_str = json_body->toStyledString(); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_str.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json_str.length()); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + + StreamingCallback sc; + OaiInfo oi{model, false /*include_usage*/, false /*oai_endpoint*/, + n_probs}; + sc.callback = std::make_shared(callback); + sc.need_stop = true; + sc.oi = oi; + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sc); + auto res = curl_easy_perform(curl); + + if (res != CURLE_OK) { + CTL_WRN("CURL request failed: " << curl_easy_strerror(res)); + + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = true; + status["status_code"] = 500; + + Json::Value error; + error["error"] = curl_easy_strerror(res); + callback(std::move(status), std::move(error)); + } + curl_easy_cleanup(curl); + if (sc.need_stop) { + CTL_DBG("No stop message received, need to stop"); + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = true; + status["status_code"] = 200; + (*sc.callback)(std::move(status), Json::Value()); + } + }); + + } else { + auto url = url_parser::Url{ + .protocol = "http", + .host = s.host + ":" + std::to_string(s.port), + .pathParams = + { + "v1", + "completions", + }, + }; + Json::Value result; + int prompt_tokens = 0; + int predicted_tokens = 0; + // multiple choices + for (int i = 0; i < n; i++) { + auto response = curl_utils::SimplePostJson(url.ToFullPath(), + json_body->toStyledString()); + if (response.has_value()) { + auto r = response.value(); + Json::Value logprobs; + prompt_tokens += r["tokens_evaluated"].asInt(); + predicted_tokens += r["tokens_predicted"].asInt(); + std::string to_send = r["content"].asString(); + string_utils::LTrim(to_send); + if (n_probs > 0) { + logprobs = r["completion_probabilities"]; + } + if (i == 0) { + result = CreateFullReturnJson( + GenerateRandomString(20), model, to_send, "_", prompt_tokens, + predicted_tokens, Json::Value("stop"), logprobs); + } else { + auto choice = CreateFullReturnJson( + GenerateRandomString(20), model, to_send, "_", prompt_tokens, + predicted_tokens, Json::Value("stop"), logprobs)["choices"][0]; + choice["index"] = i; + result["choices"].append(choice); + result["usage"]["completion_tokens"] = predicted_tokens; + result["usage"]["prompt_tokens"] = prompt_tokens; + result["usage"]["total_tokens"] = predicted_tokens + prompt_tokens; + } + + if (i == n - 1) { + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(result)); + } + } else { + CTL_WRN("Error: " << response.error()); + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 500; + callback(std::move(status), std::move(response.value())); + break; + } + } + } +} + } // namespace cortex::local \ No newline at end of file diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h index e344541b9..5c65cf3e6 100644 --- a/engine/extensions/local-engine/local_engine.h +++ b/engine/extensions/local-engine/local_engine.h @@ -11,10 +11,16 @@ #include "utils/task_queue.h" namespace cortex::local { +using http_callback = std::function; + struct ServerAddress { std::string host; int port; cortex::process::ProcessInfo process_info; + std::string pre_prompt; + std::string user_prompt; + std::string ai_prompt; + std::string system_prompt; }; class LocalEngine : public EngineI { public: @@ -26,26 +32,20 @@ class LocalEngine : public EngineI { void Unload(EngineUnloadOption opts) final {} - void HandleChatCompletion( - std::shared_ptr json_body, - std::function&& callback) final; - void HandleEmbedding( - std::shared_ptr json_body, - std::function&& callback) final; - void LoadModel( - std::shared_ptr json_body, - std::function&& callback) final; - void UnloadModel( - std::shared_ptr json_body, - std::function&& callback) final; - void GetModelStatus( - std::shared_ptr json_body, - std::function&& callback) final; + void HandleChatCompletion(std::shared_ptr json_body, + http_callback&& callback) final; + void HandleEmbedding(std::shared_ptr json_body, + http_callback&& callback) final; + void LoadModel(std::shared_ptr json_body, + http_callback&& callback) final; + void UnloadModel(std::shared_ptr json_body, + http_callback&& callback) final; + void GetModelStatus(std::shared_ptr json_body, + http_callback&& callback) final; // Get list of running models - void GetModels( - std::shared_ptr jsonBody, - std::function&& callback) final; + void GetModels(std::shared_ptr jsonBody, + http_callback&& callback) final; bool SetFileLogger(int max_log_lines, const std::string& log_path) final { return true; @@ -55,13 +55,20 @@ class LocalEngine : public EngineI { // Stop inflight chat completion in stream mode void StopInferencing(const std::string& model_id) final {} - void HandleRouteRequest( - std::shared_ptr json_body, - std::function&& callback) final {} + void HandleRouteRequest(std::shared_ptr json_body, + http_callback&& callback) final {} + + void HandleInference(std::shared_ptr json_body, + http_callback&& callback) final {} + + private: + void HandleOpenAiChatCompletion(std::shared_ptr json_body, + http_callback&& callback, + const std::string& model); - void HandleInference( - std::shared_ptr json_body, - std::function&& callback) final {} + void HandleNonOpenAiChatCompletion(std::shared_ptr json_body, + http_callback&& callback, + const std::string& model); private: std::unordered_map server_map_; diff --git a/engine/utils/string_utils.h b/engine/utils/string_utils.h index a962109e8..8806a833e 100644 --- a/engine/utils/string_utils.h +++ b/engine/utils/string_utils.h @@ -21,6 +21,12 @@ inline std::string RTrim(const std::string& str) { return (end == std::string::npos) ? "" : str.substr(0, end + 1); } +inline void LTrim(std::string& s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +}; + inline void Trim(std::string& s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); From 8c4ca069263db81a63cc8ad203633fe27f43c45f Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 09:47:00 +0700 Subject: [PATCH 04/49] fix: wait for child process up --- engine/extensions/local-engine/local_engine.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index c56fb6192..f64db1b4d 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -492,16 +492,17 @@ void LocalEngine::HandleEmbedding(std::shared_ptr json_body, void LocalEngine::LoadModel(std::shared_ptr json_body, http_callback&& callback) { CTL_INF("Start loading model"); - auto wait_for_server_up = [](const std::string& host, int port) { + auto wait_for_server_up = [this](const std::string& model, + const std::string& host, int port) { auto url = url_parser::Url{ .protocol = "http", .host = host + ":" + std::to_string(port), .pathParams = {"health"}, }; - for (size_t i = 0; i < 10; i++) { + while (server_map_.find(model) != server_map_.end()) { auto res = curl_utils::SimpleGet(url.ToFullPath()); if (res.has_error()) { - LOG_INFO << "Wait for server up: " << i; + LOG_INFO << "Wait for server up .."; std::this_thread::sleep_for(std::chrono::seconds(1)); } else { return true; @@ -560,7 +561,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, } s.process_info = result.value(); - if (wait_for_server_up(s.host, s.port)) { + if (wait_for_server_up(model_id, s.host, s.port)) { Json::Value response; response["status"] = "Model loaded successfully with pid: " + std::to_string(s.process_info.pid); From 0f2fa6e988f724c2b9b1b99ab826a12ca4b55fca Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 11:07:42 +0700 Subject: [PATCH 05/49] chore: cleanup --- engine/cli/command_line_parser.cc | 16 ++--- engine/cli/commands/cortex_upd_cmd.cc | 4 +- engine/cli/commands/cortex_upd_cmd.h | 4 +- engine/cli/main.cc | 7 ++- .../extensions/local-engine/local_engine.cc | 6 +- engine/services/engine_service.cc | 60 ++++++++----------- .../components/test_github_release_utils.cc | 4 +- engine/test/components/test_string_utils.cc | 40 +++++++++++++ engine/utils/engine_constants.h | 8 ++- engine/utils/engine_matcher_utils.h | 48 +++++++++------ engine/utils/github_release_utils.h | 5 -- 11 files changed, 123 insertions(+), 79 deletions(-) diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index 6c9ea271c..dd10c7e3b 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -124,14 +124,14 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) { } } #endif - // auto config = file_manager_utils::GetCortexConfig(); - // if (!config.llamacppVersion.empty() && - // config.latestLlamacppRelease != config.llamacppVersion) { - // CLI_LOG( - // "\nNew llama.cpp version available: " << config.latestLlamacppRelease); - // CLI_LOG("To update, run: " << commands::GetCortexBinary() - // << " engines update llama-cpp"); - // } + auto config = file_manager_utils::GetCortexConfig(); + if (!config.llamacppVersion.empty() && + config.latestLlamacppRelease != config.llamacppVersion) { + CLI_LOG( + "\nNew llama.cpp version available: " << config.latestLlamacppRelease); + CLI_LOG("To update, run: " << commands::GetCortexBinary() + << " engines update llama-cpp"); + } return true; } diff --git a/engine/cli/commands/cortex_upd_cmd.cc b/engine/cli/commands/cortex_upd_cmd.cc index 6c8baa1a4..88660eb1b 100644 --- a/engine/cli/commands/cortex_upd_cmd.cc +++ b/engine/cli/commands/cortex_upd_cmd.cc @@ -515,10 +515,10 @@ bool CortexUpdCmd::GetLinuxInstallScript(const std::string& v, const std::string& channel) { std::vector path_list; if (channel == "nightly") { - path_list = {"menloresearch", "cortex.cpp", "dev", "engine", + path_list = {kMenloOrg, "cortex.cpp", "dev", "engine", "templates", "linux", "install.sh"}; } else { - path_list = {"menloresearch", "cortex.cpp", "main", "engine", + path_list = {kMenloOrg, "cortex.cpp", "main", "engine", "templates", "linux", "install.sh"}; } auto url_obj = url_parser::Url{ diff --git a/engine/cli/commands/cortex_upd_cmd.h b/engine/cli/commands/cortex_upd_cmd.h index 7f02839cf..fdee6cc49 100644 --- a/engine/cli/commands/cortex_upd_cmd.h +++ b/engine/cli/commands/cortex_upd_cmd.h @@ -79,9 +79,9 @@ inline std::vector GetReleasePath() { if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) { return {"cortex", "latest", "version.json"}; } else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) { - return {"repos", "menloresearch", "cortex.cpp", "releases"}; + return {"repos", kMenloOrg, "cortex.cpp", "releases"}; } else { - return {"repos", "menloresearch", "cortex.cpp", "releases", "latest"}; + return {"repos", kMenloOrg, "cortex.cpp", "releases", "latest"}; } } diff --git a/engine/cli/main.cc b/engine/cli/main.cc index 6b37c6156..f082436bc 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -148,14 +148,14 @@ int main(int argc, char* argv[]) { std::chrono::hours(24); should_check_for_latest_llamacpp_version = now > last_check; } - - if (false) { + + 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"); + kGgmlOrg, "llama.cpp", "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: " + @@ -171,6 +171,7 @@ int main(int argc, char* argv[]) { }; auto res = get_latest_version(); + if (res.has_error()) { CTL_ERR("Failed to get latest llama.cpp version: " << res.error()); return; diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index f64db1b4d..8e34d926e 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -530,13 +530,13 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, std::vector v; v.reserve(params.size() + 1); - auto engine_dir = engine_service_.GetEngineDirPath("llama.cpp"); + auto engine_dir = engine_service_.GetEngineDirPath(kLlamaRepo); if (engine_dir.has_error()) { CTL_WRN(engine_dir.error()); server_map_.erase(model_id); return; } - auto exe = (engine_dir.value().first / "llama-server").string(); + auto exe = (engine_dir.value().first / kLlamaServer).string(); v.push_back(exe); v.insert(v.end(), params.begin(), params.end()); @@ -544,7 +544,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto log_path = (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); - CTL_INF("log: " << log_path); + CTL_DBG("log: " << log_path); auto result = cortex::process::SpawnProcess(v, log_path, log_path); if (result.has_error()) { CTL_ERR("Fail to spawn process. " << result.error()); diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index 592c72d72..c45240828 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -47,13 +47,6 @@ std::string Repo2Engine(const std::string& r) { } return r; }; - -std::string GetEnginePath(std::string_view e) { - if (e == kLlamaRepo) { - return kLlamaLibPath; - } - return kLlamaLibPath; -}; } // namespace cpp::result EngineService::InstallEngineAsync( @@ -238,11 +231,10 @@ cpp::result EngineService::DownloadEngine( : normalized_version; std::unordered_set merged_variant_name = { "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + - ".tar.gz", + ".tar.gz", // menlo "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + - ".zip"}; + ".zip"}; // ggml - // CTL_INF("merged_variant_name: " << merged_variant_name); for (const auto& asset : res.value()) { if (merged_variant_name.find(asset.name) != merged_variant_name.end()) { selected_variant = asset; @@ -279,23 +271,21 @@ cpp::result EngineService::DownloadEngine( } } - // auto normalize_version = "v" + selected_variant->version; - auto normalize_version = selected_variant->version; auto variant_folder_name = engine_matcher_utils::GetVariantFromNameAndVersion( selected_variant->name, engine, selected_variant->version); auto variant_folder_path = file_manager_utils::GetEnginesContainerPath() / engine / variant_folder_name.value() / - normalize_version; + selected_variant->version; auto variant_path = variant_folder_path / selected_variant->name; std::filesystem::create_directories(variant_folder_path); CTL_INF("variant_folder_path: " + variant_folder_path.string()); - auto on_finished = [this, engine, selected_variant, variant_folder_path, - normalize_version](const DownloadTask& finishedTask) { + auto on_finished = [this, engine, selected_variant, + variant_folder_path](const DownloadTask& finishedTask) { // try to unzip the downloaded file CTL_INF("Engine zip path: " << finishedTask.items[0].localPath.string()); - CTL_INF("Version: " + normalize_version); + CTL_INF("Version: " + selected_variant->version); auto extract_path = finishedTask.items[0].localPath.parent_path(); archive_utils::ExtractArchive(finishedTask.items[0].localPath.string(), @@ -303,12 +293,13 @@ cpp::result EngineService::DownloadEngine( CTL_INF("local path: " << finishedTask.items[0].localPath.string() << ", extract path: " << extract_path.string()); auto variant = engine_matcher_utils::GetVariantFromNameAndVersion( - selected_variant->name, engine, normalize_version); + selected_variant->name, engine, selected_variant->version); CTL_INF("Extracted variant: " + variant.value()); try { + // Create version file std::ofstream meta(extract_path / "version.txt", std::ios::out); meta << "name: " << variant.value() << std::endl; - meta << "version: " << normalize_version << std::endl; + meta << "version: " << selected_variant->version << std::endl; meta.close(); namespace fs = std::filesystem; @@ -326,7 +317,7 @@ cpp::result EngineService::DownloadEngine( if (!fs::exists(extract_path.parent_path().parent_path() / "deps")) { fs::create_directory(extract_path.parent_path().parent_path() / "deps"); } - std::filesystem::permissions(extract_path / "llama-server", + std::filesystem::permissions(extract_path / kLlamaServer, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec, @@ -337,17 +328,17 @@ cpp::result EngineService::DownloadEngine( } // set as default - - auto res = - SetDefaultEngineVariant(engine, normalize_version, variant.value()); + auto res = SetDefaultEngineVariant(engine, selected_variant->version, + variant.value()); if (res.has_error()) { CTL_ERR("Failed to set default engine variant: " << res.error()); } else { CTL_INF("Set default engine variant: " << res.value().variant); } - auto create_res = EngineService::UpsertEngine( - engine, // engine_name - kLocal, "", "", normalize_version, variant.value(), "Default", ""); + auto create_res = + EngineService::UpsertEngine(engine, // engine_name + kLocal, "", "", selected_variant->version, + variant.value(), "Default", ""); if (create_res.has_error()) { CTL_ERR("Failed to create engine entry: " << create_res->engine_name); @@ -358,7 +349,7 @@ cpp::result EngineService::DownloadEngine( for (const auto& entry : std::filesystem::directory_iterator( variant_folder_path.parent_path())) { if (entry.is_directory() && - entry.path().filename() != normalize_version) { + entry.path().filename() != selected_variant->version) { try { std::filesystem::remove_all(entry.path()); } catch (const std::exception& e) { @@ -472,8 +463,8 @@ std::string EngineService::GetMatchedVariant( cpp::result, std::string> EngineService::GetEngineReleases(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto ggml_org = github_release_utils::GetReleases("ggml-org", ne); - auto menlo = github_release_utils::GetReleases("menloresearch", ne); + auto ggml_org = github_release_utils::GetReleases(kGgmlOrg, ne); + auto menlo = github_release_utils::GetReleases(kMenloOrg, ne); if (ggml_org.has_error() && menlo.has_error()) { return cpp::fail(ggml_org.error()); } @@ -500,13 +491,13 @@ EngineService::GetEngineVariants(const std::string& engine, bool filter_compatible_only) const { auto ne = cortex::engine::NormalizeEngine(engine); auto engine_release_menlo = - github_release_utils::GetReleaseByVersion("menloresearch", ne, version); + github_release_utils::GetReleaseByVersion(kMenloOrg, ne, version); auto engine_release_ggml = - github_release_utils::GetReleaseByVersion("ggml-org", ne, version); + github_release_utils::GetReleaseByVersion(kGgmlOrg, ne, version); if (engine_release_menlo.has_error() && engine_release_ggml.has_error()) { return cpp::fail("Failed to get engine release: " + - engine_release_menlo.error()); + engine_release_menlo.error()); } if (engine_release_menlo.has_error()) { CTL_WRN("Failed to get engine release: " << engine_release_menlo.error()); @@ -835,8 +826,8 @@ EngineService::GetEngineDirPath(const std::string& engine_name) { CTL_DBG("user defined engine path: " << user_defined_engine_path); const std::filesystem::path engine_dir_path = [&] { if (user_defined_engine_path != nullptr) { - return std::filesystem::path(user_defined_engine_path) / - GetEnginePath(ne) / selected_engine_variant->variant / + return std::filesystem::path(user_defined_engine_path) / kLlamaLibPath / + selected_engine_variant->variant / selected_engine_variant->version; } else { return file_manager_utils::GetEnginesContainerPath() / ne / @@ -897,8 +888,7 @@ std::vector EngineService::GetLoadedEngines() { cpp::result EngineService::GetLatestEngineVersion(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto res = - github_release_utils::GetReleaseByVersion("menloresearch", ne, "latest"); + auto res = github_release_utils::GetReleaseByVersion(kMenloOrg, ne, "latest"); if (res.has_error()) { return cpp::fail("Failed to fetch engine " + engine + " latest version!"); } diff --git a/engine/test/components/test_github_release_utils.cc b/engine/test/components/test_github_release_utils.cc index ae1e2c7c2..753e7270c 100644 --- a/engine/test/components/test_github_release_utils.cc +++ b/engine/test/components/test_github_release_utils.cc @@ -6,14 +6,14 @@ class GitHubReleaseUtilsTest : public ::testing::Test {}; TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseByVersion) { auto version{"v0.1.36"}; auto result = github_release_utils::GetReleaseByVersion( - "menloresearch", "cortex.llamacpp", version); + kMenloOrg, "cortex.llamacpp", version); ASSERT_TRUE(result.has_value()); ASSERT_EQ(result->tag_name, version); } TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseList) { - auto result = github_release_utils::GetReleases("menloresearch", "cortex.llamacpp"); + auto result = github_release_utils::GetReleases(kMenloOrg, "cortex.llamacpp"); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->size() > 0); diff --git a/engine/test/components/test_string_utils.cc b/engine/test/components/test_string_utils.cc index e396f0ed1..1693bb166 100644 --- a/engine/test/components/test_string_utils.cc +++ b/engine/test/components/test_string_utils.cc @@ -288,4 +288,44 @@ TEST_F(StringUtilsTestSuite, LargeInputPerformance) { EXPECT_EQ(RemoveSubstring(large_input, to_remove), ""); } +TEST(LTrimTest, EmptyString) { + std::string s = ""; + LTrim(s); + EXPECT_EQ(s, ""); +} + +TEST(LTrimTest, NoSpaces) { + std::string s = "HelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, LeadingSpaces) { + std::string s = " HelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} +TEST(LTrimTest, LeadingTabs) { + std::string s = "\t\tHelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, LeadingNewlines) { + std::string s = "\n\nHelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, OnlySpaces) { + std::string s = " "; + LTrim(s); + EXPECT_EQ(s, ""); +} + +TEST(LTrimTest, MixedSpaces) { + std::string s = " \t\nHelloWorld "; + LTrim(s); + EXPECT_EQ(s, "HelloWorld "); +} diff --git a/engine/utils/engine_constants.h b/engine/utils/engine_constants.h index 0e91c388f..ee935f38a 100644 --- a/engine/utils/engine_constants.h +++ b/engine/utils/engine_constants.h @@ -5,9 +5,12 @@ constexpr const auto kLlamaEngine = "llama-cpp"; constexpr const auto kRemote = "remote"; constexpr const auto kLocal = "local"; - constexpr const auto kLlamaRepo = "llama.cpp"; -constexpr const auto kLlamaLibPath = "./engines/cortex.llamacpp"; +constexpr const auto kLlamaLibPath = "./engines/llama.cpp"; +constexpr const auto kLlamaServer = "llama-server"; + +constexpr const auto kMenloOrg = "menloresearch"; +constexpr const auto kGgmlOrg = "ggml-org"; // other constants constexpr auto static kHuggingFaceHost = "huggingface.co"; @@ -18,6 +21,7 @@ constexpr auto static kDefaultGHUserAgent = "cortexcpp"; constexpr auto static kWindowsOs = "win"; constexpr auto static kMacOs = "mac"; constexpr auto static kLinuxOs = "linux"; +constexpr auto static kUbuntuOs = "ubuntu"; constexpr auto static kUnsupportedOs = "Unsupported OS"; constexpr auto static kCurlGetTimeout = 10; diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index 7c99f4a0e..f33b738ed 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -7,6 +7,7 @@ #include #include #include "utils/cpuid/cpu_info.h" +#include "utils/engine_constants.h" #include "utils/logging_utils.h" #include "utils/result.hpp" #include "utils/string_utils.h" @@ -24,19 +25,18 @@ inline cpp::result GetVariantFromNameAndVersion( if (engine.empty()) { return cpp::fail("Engine name is empty"); } - CTL_INF("version: " << version); - auto nv = string_utils::RemoveSubstring(version, "v"); - CTL_INF("nv: " << nv); - using namespace string_utils; - CTL_INF("engine_file_name: " << engine_file_name); - auto removed_extension = RemoveSubstring(engine_file_name, ".tar.gz"); - auto removed_extension_e = RemoveSubstring(removed_extension, ".zip"); - CTL_INF("removed_extension: " << removed_extension_e); - auto version_and_variant = RemoveSubstring(removed_extension_e, engine + "-"); - CTL_INF("version_and_variant: " << version_and_variant); - auto variant = RemoveSubstring(version_and_variant, nv + "-"); - auto v = RemoveSubstring(variant, "llama-bin-"); - CTL_INF("variant: " << v); + CTL_DBG("version: " << version); + namespace su = string_utils; + CTL_DBG("engine_file_name: " << engine_file_name); + auto rm_extension_menlo = su::RemoveSubstring(engine_file_name, ".tar.gz"); + auto rm_extension_ggml = su::RemoveSubstring(rm_extension_menlo, ".zip"); + CTL_DBG("removed_extension: " << rm_extension_ggml); + auto version_and_variant = + su::RemoveSubstring(rm_extension_ggml, engine + "-"); + CTL_DBG("version_and_variant: " << version_and_variant); + auto variant = su::RemoveSubstring(version_and_variant, version + "-"); + auto v = su::RemoveSubstring(variant, "llama-bin-"); + CTL_DBG("variant: " << v); return v; } @@ -148,8 +148,9 @@ inline std::string Validate(const std::vector& variants, const std::string& os, const std::string& cpu_arch, const std::string& suitable_avx, const std::string& cuda_version) { + CTL_INF(os << " " << cpu_arch); // Early return if the OS is not supported - if (os != "mac" && os != "windows" && os != "linux") { + if (os != kMacOs && os != kWindowsOs && os != kLinuxOs) { return ""; } @@ -157,6 +158,12 @@ inline std::string Validate(const std::vector& variants, std::copy_if(variants.begin(), variants.end(), std::back_inserter(os_and_arch_compatible_list), [&os, &cpu_arch](const std::string& variant) { + // In case of Linux, we need to include ubuntu version also + if (os == kLinuxOs) { + if (variant.find(kUbuntuOs) != std::string::npos && + variant.find(cpu_arch) != std::string::npos) + return true; + } auto os_match = "-" + os; auto cpu_arch_match = "-" + cpu_arch; @@ -164,10 +171,10 @@ inline std::string Validate(const std::vector& variants, variant.find(cpu_arch_match) != std::string::npos; }); - if (os == "mac" && !os_and_arch_compatible_list.empty()) + if (os == kMacOs && !os_and_arch_compatible_list.empty()) return os_and_arch_compatible_list[0]; - if (os == "linux" && cpu_arch == "arm64" && + if (os == kLinuxOs && cpu_arch == "arm64" && !os_and_arch_compatible_list.empty()) { return os_and_arch_compatible_list[0]; } @@ -177,7 +184,14 @@ inline std::string Validate(const std::vector& variants, std::copy_if(os_and_arch_compatible_list.begin(), os_and_arch_compatible_list.end(), std::back_inserter(avx_compatible_list), - [&suitable_avx](const std::string& variant) { + [&os, &cpu_arch, &suitable_avx](const std::string& variant) { + if (os == kLinuxOs && + (suitable_avx == "avx2" || suitable_avx == "avx512" || + cpu_arch == "arm64")) { + if (variant.find(std::string(kUbuntuOs) + "-" + cpu_arch) != + std::string::npos) + return true; + } auto suitable_avx_match = "-" + suitable_avx; return variant.find(suitable_avx_match) != std::string::npos; diff --git a/engine/utils/github_release_utils.h b/engine/utils/github_release_utils.h index ea07d3f05..a045a7437 100644 --- a/engine/utils/github_release_utils.h +++ b/engine/utils/github_release_utils.h @@ -179,11 +179,6 @@ inline cpp::result GetReleaseByVersion( std::vector path_params{"repos", author, repo, "releases"}; if (tag != "latest") { path_params.push_back("tags"); - - // if (!string_utils::StartsWith(tag, "v")) { - // path_params.push_back("v" + tag); - // } - path_params.push_back(tag); } else { path_params.push_back("latest"); From 6dd7f7c0cb1e730d650a9f253d85cd1cf26d331d Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 11:07:42 +0700 Subject: [PATCH 06/49] chore: cleanup --- engine/cli/command_line_parser.cc | 16 ++--- engine/cli/commands/cortex_upd_cmd.cc | 4 +- engine/cli/commands/cortex_upd_cmd.h | 4 +- engine/cli/main.cc | 7 ++- .../extensions/local-engine/local_engine.cc | 6 +- engine/services/engine_service.cc | 60 ++++++++----------- engine/services/inference_service.cc | 34 +++++------ .../components/test_github_release_utils.cc | 4 +- engine/test/components/test_string_utils.cc | 40 +++++++++++++ engine/utils/engine_constants.h | 8 ++- engine/utils/engine_matcher_utils.h | 48 +++++++++------ engine/utils/github_release_utils.h | 5 -- 12 files changed, 140 insertions(+), 96 deletions(-) diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index 6c9ea271c..dd10c7e3b 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -124,14 +124,14 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) { } } #endif - // auto config = file_manager_utils::GetCortexConfig(); - // if (!config.llamacppVersion.empty() && - // config.latestLlamacppRelease != config.llamacppVersion) { - // CLI_LOG( - // "\nNew llama.cpp version available: " << config.latestLlamacppRelease); - // CLI_LOG("To update, run: " << commands::GetCortexBinary() - // << " engines update llama-cpp"); - // } + auto config = file_manager_utils::GetCortexConfig(); + if (!config.llamacppVersion.empty() && + config.latestLlamacppRelease != config.llamacppVersion) { + CLI_LOG( + "\nNew llama.cpp version available: " << config.latestLlamacppRelease); + CLI_LOG("To update, run: " << commands::GetCortexBinary() + << " engines update llama-cpp"); + } return true; } diff --git a/engine/cli/commands/cortex_upd_cmd.cc b/engine/cli/commands/cortex_upd_cmd.cc index 6c8baa1a4..88660eb1b 100644 --- a/engine/cli/commands/cortex_upd_cmd.cc +++ b/engine/cli/commands/cortex_upd_cmd.cc @@ -515,10 +515,10 @@ bool CortexUpdCmd::GetLinuxInstallScript(const std::string& v, const std::string& channel) { std::vector path_list; if (channel == "nightly") { - path_list = {"menloresearch", "cortex.cpp", "dev", "engine", + path_list = {kMenloOrg, "cortex.cpp", "dev", "engine", "templates", "linux", "install.sh"}; } else { - path_list = {"menloresearch", "cortex.cpp", "main", "engine", + path_list = {kMenloOrg, "cortex.cpp", "main", "engine", "templates", "linux", "install.sh"}; } auto url_obj = url_parser::Url{ diff --git a/engine/cli/commands/cortex_upd_cmd.h b/engine/cli/commands/cortex_upd_cmd.h index 7f02839cf..fdee6cc49 100644 --- a/engine/cli/commands/cortex_upd_cmd.h +++ b/engine/cli/commands/cortex_upd_cmd.h @@ -79,9 +79,9 @@ inline std::vector GetReleasePath() { if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) { return {"cortex", "latest", "version.json"}; } else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) { - return {"repos", "menloresearch", "cortex.cpp", "releases"}; + return {"repos", kMenloOrg, "cortex.cpp", "releases"}; } else { - return {"repos", "menloresearch", "cortex.cpp", "releases", "latest"}; + return {"repos", kMenloOrg, "cortex.cpp", "releases", "latest"}; } } diff --git a/engine/cli/main.cc b/engine/cli/main.cc index 6b37c6156..f082436bc 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -148,14 +148,14 @@ int main(int argc, char* argv[]) { std::chrono::hours(24); should_check_for_latest_llamacpp_version = now > last_check; } - - if (false) { + + 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"); + kGgmlOrg, "llama.cpp", "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: " + @@ -171,6 +171,7 @@ int main(int argc, char* argv[]) { }; auto res = get_latest_version(); + if (res.has_error()) { CTL_ERR("Failed to get latest llama.cpp version: " << res.error()); return; diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index f64db1b4d..8e34d926e 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -530,13 +530,13 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, std::vector v; v.reserve(params.size() + 1); - auto engine_dir = engine_service_.GetEngineDirPath("llama.cpp"); + auto engine_dir = engine_service_.GetEngineDirPath(kLlamaRepo); if (engine_dir.has_error()) { CTL_WRN(engine_dir.error()); server_map_.erase(model_id); return; } - auto exe = (engine_dir.value().first / "llama-server").string(); + auto exe = (engine_dir.value().first / kLlamaServer).string(); v.push_back(exe); v.insert(v.end(), params.begin(), params.end()); @@ -544,7 +544,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto log_path = (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); - CTL_INF("log: " << log_path); + CTL_DBG("log: " << log_path); auto result = cortex::process::SpawnProcess(v, log_path, log_path); if (result.has_error()) { CTL_ERR("Fail to spawn process. " << result.error()); diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index 592c72d72..c45240828 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -47,13 +47,6 @@ std::string Repo2Engine(const std::string& r) { } return r; }; - -std::string GetEnginePath(std::string_view e) { - if (e == kLlamaRepo) { - return kLlamaLibPath; - } - return kLlamaLibPath; -}; } // namespace cpp::result EngineService::InstallEngineAsync( @@ -238,11 +231,10 @@ cpp::result EngineService::DownloadEngine( : normalized_version; std::unordered_set merged_variant_name = { "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + - ".tar.gz", + ".tar.gz", // menlo "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + - ".zip"}; + ".zip"}; // ggml - // CTL_INF("merged_variant_name: " << merged_variant_name); for (const auto& asset : res.value()) { if (merged_variant_name.find(asset.name) != merged_variant_name.end()) { selected_variant = asset; @@ -279,23 +271,21 @@ cpp::result EngineService::DownloadEngine( } } - // auto normalize_version = "v" + selected_variant->version; - auto normalize_version = selected_variant->version; auto variant_folder_name = engine_matcher_utils::GetVariantFromNameAndVersion( selected_variant->name, engine, selected_variant->version); auto variant_folder_path = file_manager_utils::GetEnginesContainerPath() / engine / variant_folder_name.value() / - normalize_version; + selected_variant->version; auto variant_path = variant_folder_path / selected_variant->name; std::filesystem::create_directories(variant_folder_path); CTL_INF("variant_folder_path: " + variant_folder_path.string()); - auto on_finished = [this, engine, selected_variant, variant_folder_path, - normalize_version](const DownloadTask& finishedTask) { + auto on_finished = [this, engine, selected_variant, + variant_folder_path](const DownloadTask& finishedTask) { // try to unzip the downloaded file CTL_INF("Engine zip path: " << finishedTask.items[0].localPath.string()); - CTL_INF("Version: " + normalize_version); + CTL_INF("Version: " + selected_variant->version); auto extract_path = finishedTask.items[0].localPath.parent_path(); archive_utils::ExtractArchive(finishedTask.items[0].localPath.string(), @@ -303,12 +293,13 @@ cpp::result EngineService::DownloadEngine( CTL_INF("local path: " << finishedTask.items[0].localPath.string() << ", extract path: " << extract_path.string()); auto variant = engine_matcher_utils::GetVariantFromNameAndVersion( - selected_variant->name, engine, normalize_version); + selected_variant->name, engine, selected_variant->version); CTL_INF("Extracted variant: " + variant.value()); try { + // Create version file std::ofstream meta(extract_path / "version.txt", std::ios::out); meta << "name: " << variant.value() << std::endl; - meta << "version: " << normalize_version << std::endl; + meta << "version: " << selected_variant->version << std::endl; meta.close(); namespace fs = std::filesystem; @@ -326,7 +317,7 @@ cpp::result EngineService::DownloadEngine( if (!fs::exists(extract_path.parent_path().parent_path() / "deps")) { fs::create_directory(extract_path.parent_path().parent_path() / "deps"); } - std::filesystem::permissions(extract_path / "llama-server", + std::filesystem::permissions(extract_path / kLlamaServer, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec, @@ -337,17 +328,17 @@ cpp::result EngineService::DownloadEngine( } // set as default - - auto res = - SetDefaultEngineVariant(engine, normalize_version, variant.value()); + auto res = SetDefaultEngineVariant(engine, selected_variant->version, + variant.value()); if (res.has_error()) { CTL_ERR("Failed to set default engine variant: " << res.error()); } else { CTL_INF("Set default engine variant: " << res.value().variant); } - auto create_res = EngineService::UpsertEngine( - engine, // engine_name - kLocal, "", "", normalize_version, variant.value(), "Default", ""); + auto create_res = + EngineService::UpsertEngine(engine, // engine_name + kLocal, "", "", selected_variant->version, + variant.value(), "Default", ""); if (create_res.has_error()) { CTL_ERR("Failed to create engine entry: " << create_res->engine_name); @@ -358,7 +349,7 @@ cpp::result EngineService::DownloadEngine( for (const auto& entry : std::filesystem::directory_iterator( variant_folder_path.parent_path())) { if (entry.is_directory() && - entry.path().filename() != normalize_version) { + entry.path().filename() != selected_variant->version) { try { std::filesystem::remove_all(entry.path()); } catch (const std::exception& e) { @@ -472,8 +463,8 @@ std::string EngineService::GetMatchedVariant( cpp::result, std::string> EngineService::GetEngineReleases(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto ggml_org = github_release_utils::GetReleases("ggml-org", ne); - auto menlo = github_release_utils::GetReleases("menloresearch", ne); + auto ggml_org = github_release_utils::GetReleases(kGgmlOrg, ne); + auto menlo = github_release_utils::GetReleases(kMenloOrg, ne); if (ggml_org.has_error() && menlo.has_error()) { return cpp::fail(ggml_org.error()); } @@ -500,13 +491,13 @@ EngineService::GetEngineVariants(const std::string& engine, bool filter_compatible_only) const { auto ne = cortex::engine::NormalizeEngine(engine); auto engine_release_menlo = - github_release_utils::GetReleaseByVersion("menloresearch", ne, version); + github_release_utils::GetReleaseByVersion(kMenloOrg, ne, version); auto engine_release_ggml = - github_release_utils::GetReleaseByVersion("ggml-org", ne, version); + github_release_utils::GetReleaseByVersion(kGgmlOrg, ne, version); if (engine_release_menlo.has_error() && engine_release_ggml.has_error()) { return cpp::fail("Failed to get engine release: " + - engine_release_menlo.error()); + engine_release_menlo.error()); } if (engine_release_menlo.has_error()) { CTL_WRN("Failed to get engine release: " << engine_release_menlo.error()); @@ -835,8 +826,8 @@ EngineService::GetEngineDirPath(const std::string& engine_name) { CTL_DBG("user defined engine path: " << user_defined_engine_path); const std::filesystem::path engine_dir_path = [&] { if (user_defined_engine_path != nullptr) { - return std::filesystem::path(user_defined_engine_path) / - GetEnginePath(ne) / selected_engine_variant->variant / + return std::filesystem::path(user_defined_engine_path) / kLlamaLibPath / + selected_engine_variant->variant / selected_engine_variant->version; } else { return file_manager_utils::GetEnginesContainerPath() / ne / @@ -897,8 +888,7 @@ std::vector EngineService::GetLoadedEngines() { cpp::result EngineService::GetLatestEngineVersion(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto res = - github_release_utils::GetReleaseByVersion("menloresearch", ne, "latest"); + auto res = github_release_utils::GetReleaseByVersion(kMenloOrg, ne, "latest"); if (res.has_error()) { return cpp::fail("Failed to fetch engine " + engine + " latest version!"); } diff --git a/engine/services/inference_service.cc b/engine/services/inference_service.cc index 2a3ab2ccc..939c91d85 100644 --- a/engine/services/inference_service.cc +++ b/engine/services/inference_service.cc @@ -17,23 +17,23 @@ cpp::result InferenceService::HandleChatCompletion( CTL_DBG("engine_type: " << engine_type); auto tool_choice = json_body->get("tool_choice", Json::Value::null); auto model_id = json_body->get("model", "").asString(); - // if (saved_models_.find(model_id) != saved_models_.end()) { - // // check if model is started, if not start it first - // Json::Value root; - // root["model"] = model_id; - // root["engine"] = engine_type; - // auto ir = GetModelStatus(std::make_shared(root)); - // auto status = std::get<0>(ir)["status_code"].asInt(); - // if (status != drogon::k200OK) { - // CTL_INF("Model is not loaded, start loading it: " << model_id); - // // For remote engine, we use the updated configuration - // if (engine_service_->IsRemoteEngine(engine_type)) { - // (void)model_service_.lock()->StartModel(model_id, {}, false); - // } else { - // (void)LoadModel(saved_models_.at(model_id)); - // } - // } - // } + if (saved_models_.find(model_id) != saved_models_.end()) { + // check if model is started, if not start it first + Json::Value root; + root["model"] = model_id; + root["engine"] = engine_type; + auto ir = GetModelStatus(std::make_shared(root)); + auto status = std::get<0>(ir)["status_code"].asInt(); + if (status != drogon::k200OK) { + CTL_INF("Model is not loaded, start loading it: " << model_id); + // For remote engine, we use the updated configuration + if (engine_service_->IsRemoteEngine(engine_type)) { + (void)model_service_.lock()->StartModel(model_id, {}, false); + } else { + (void)LoadModel(saved_models_.at(model_id)); + } + } + } CTL_DBG("engine_type: " << engine_type); auto engine_result = engine_service_->GetLoadedEngine(engine_type); diff --git a/engine/test/components/test_github_release_utils.cc b/engine/test/components/test_github_release_utils.cc index ae1e2c7c2..753e7270c 100644 --- a/engine/test/components/test_github_release_utils.cc +++ b/engine/test/components/test_github_release_utils.cc @@ -6,14 +6,14 @@ class GitHubReleaseUtilsTest : public ::testing::Test {}; TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseByVersion) { auto version{"v0.1.36"}; auto result = github_release_utils::GetReleaseByVersion( - "menloresearch", "cortex.llamacpp", version); + kMenloOrg, "cortex.llamacpp", version); ASSERT_TRUE(result.has_value()); ASSERT_EQ(result->tag_name, version); } TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseList) { - auto result = github_release_utils::GetReleases("menloresearch", "cortex.llamacpp"); + auto result = github_release_utils::GetReleases(kMenloOrg, "cortex.llamacpp"); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->size() > 0); diff --git a/engine/test/components/test_string_utils.cc b/engine/test/components/test_string_utils.cc index e396f0ed1..1693bb166 100644 --- a/engine/test/components/test_string_utils.cc +++ b/engine/test/components/test_string_utils.cc @@ -288,4 +288,44 @@ TEST_F(StringUtilsTestSuite, LargeInputPerformance) { EXPECT_EQ(RemoveSubstring(large_input, to_remove), ""); } +TEST(LTrimTest, EmptyString) { + std::string s = ""; + LTrim(s); + EXPECT_EQ(s, ""); +} + +TEST(LTrimTest, NoSpaces) { + std::string s = "HelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, LeadingSpaces) { + std::string s = " HelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} +TEST(LTrimTest, LeadingTabs) { + std::string s = "\t\tHelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, LeadingNewlines) { + std::string s = "\n\nHelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, OnlySpaces) { + std::string s = " "; + LTrim(s); + EXPECT_EQ(s, ""); +} + +TEST(LTrimTest, MixedSpaces) { + std::string s = " \t\nHelloWorld "; + LTrim(s); + EXPECT_EQ(s, "HelloWorld "); +} diff --git a/engine/utils/engine_constants.h b/engine/utils/engine_constants.h index 0e91c388f..ee935f38a 100644 --- a/engine/utils/engine_constants.h +++ b/engine/utils/engine_constants.h @@ -5,9 +5,12 @@ constexpr const auto kLlamaEngine = "llama-cpp"; constexpr const auto kRemote = "remote"; constexpr const auto kLocal = "local"; - constexpr const auto kLlamaRepo = "llama.cpp"; -constexpr const auto kLlamaLibPath = "./engines/cortex.llamacpp"; +constexpr const auto kLlamaLibPath = "./engines/llama.cpp"; +constexpr const auto kLlamaServer = "llama-server"; + +constexpr const auto kMenloOrg = "menloresearch"; +constexpr const auto kGgmlOrg = "ggml-org"; // other constants constexpr auto static kHuggingFaceHost = "huggingface.co"; @@ -18,6 +21,7 @@ constexpr auto static kDefaultGHUserAgent = "cortexcpp"; constexpr auto static kWindowsOs = "win"; constexpr auto static kMacOs = "mac"; constexpr auto static kLinuxOs = "linux"; +constexpr auto static kUbuntuOs = "ubuntu"; constexpr auto static kUnsupportedOs = "Unsupported OS"; constexpr auto static kCurlGetTimeout = 10; diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index 7c99f4a0e..f33b738ed 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -7,6 +7,7 @@ #include #include #include "utils/cpuid/cpu_info.h" +#include "utils/engine_constants.h" #include "utils/logging_utils.h" #include "utils/result.hpp" #include "utils/string_utils.h" @@ -24,19 +25,18 @@ inline cpp::result GetVariantFromNameAndVersion( if (engine.empty()) { return cpp::fail("Engine name is empty"); } - CTL_INF("version: " << version); - auto nv = string_utils::RemoveSubstring(version, "v"); - CTL_INF("nv: " << nv); - using namespace string_utils; - CTL_INF("engine_file_name: " << engine_file_name); - auto removed_extension = RemoveSubstring(engine_file_name, ".tar.gz"); - auto removed_extension_e = RemoveSubstring(removed_extension, ".zip"); - CTL_INF("removed_extension: " << removed_extension_e); - auto version_and_variant = RemoveSubstring(removed_extension_e, engine + "-"); - CTL_INF("version_and_variant: " << version_and_variant); - auto variant = RemoveSubstring(version_and_variant, nv + "-"); - auto v = RemoveSubstring(variant, "llama-bin-"); - CTL_INF("variant: " << v); + CTL_DBG("version: " << version); + namespace su = string_utils; + CTL_DBG("engine_file_name: " << engine_file_name); + auto rm_extension_menlo = su::RemoveSubstring(engine_file_name, ".tar.gz"); + auto rm_extension_ggml = su::RemoveSubstring(rm_extension_menlo, ".zip"); + CTL_DBG("removed_extension: " << rm_extension_ggml); + auto version_and_variant = + su::RemoveSubstring(rm_extension_ggml, engine + "-"); + CTL_DBG("version_and_variant: " << version_and_variant); + auto variant = su::RemoveSubstring(version_and_variant, version + "-"); + auto v = su::RemoveSubstring(variant, "llama-bin-"); + CTL_DBG("variant: " << v); return v; } @@ -148,8 +148,9 @@ inline std::string Validate(const std::vector& variants, const std::string& os, const std::string& cpu_arch, const std::string& suitable_avx, const std::string& cuda_version) { + CTL_INF(os << " " << cpu_arch); // Early return if the OS is not supported - if (os != "mac" && os != "windows" && os != "linux") { + if (os != kMacOs && os != kWindowsOs && os != kLinuxOs) { return ""; } @@ -157,6 +158,12 @@ inline std::string Validate(const std::vector& variants, std::copy_if(variants.begin(), variants.end(), std::back_inserter(os_and_arch_compatible_list), [&os, &cpu_arch](const std::string& variant) { + // In case of Linux, we need to include ubuntu version also + if (os == kLinuxOs) { + if (variant.find(kUbuntuOs) != std::string::npos && + variant.find(cpu_arch) != std::string::npos) + return true; + } auto os_match = "-" + os; auto cpu_arch_match = "-" + cpu_arch; @@ -164,10 +171,10 @@ inline std::string Validate(const std::vector& variants, variant.find(cpu_arch_match) != std::string::npos; }); - if (os == "mac" && !os_and_arch_compatible_list.empty()) + if (os == kMacOs && !os_and_arch_compatible_list.empty()) return os_and_arch_compatible_list[0]; - if (os == "linux" && cpu_arch == "arm64" && + if (os == kLinuxOs && cpu_arch == "arm64" && !os_and_arch_compatible_list.empty()) { return os_and_arch_compatible_list[0]; } @@ -177,7 +184,14 @@ inline std::string Validate(const std::vector& variants, std::copy_if(os_and_arch_compatible_list.begin(), os_and_arch_compatible_list.end(), std::back_inserter(avx_compatible_list), - [&suitable_avx](const std::string& variant) { + [&os, &cpu_arch, &suitable_avx](const std::string& variant) { + if (os == kLinuxOs && + (suitable_avx == "avx2" || suitable_avx == "avx512" || + cpu_arch == "arm64")) { + if (variant.find(std::string(kUbuntuOs) + "-" + cpu_arch) != + std::string::npos) + return true; + } auto suitable_avx_match = "-" + suitable_avx; return variant.find(suitable_avx_match) != std::string::npos; diff --git a/engine/utils/github_release_utils.h b/engine/utils/github_release_utils.h index ea07d3f05..a045a7437 100644 --- a/engine/utils/github_release_utils.h +++ b/engine/utils/github_release_utils.h @@ -179,11 +179,6 @@ inline cpp::result GetReleaseByVersion( std::vector path_params{"repos", author, repo, "releases"}; if (tag != "latest") { path_params.push_back("tags"); - - // if (!string_utils::StartsWith(tag, "v")) { - // path_params.push_back("v" + tag); - // } - path_params.push_back(tag); } else { path_params.push_back("latest"); From bc927327e7d423f8d923378021901cc1619d1dd2 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 12:34:53 +0700 Subject: [PATCH 07/49] chore: fix unit tests --- engine/test/components/test_engine_matcher_utils.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index 1d1ed47a8..5329d6b8e 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -97,7 +97,7 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidateTensorrt) { TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { { - auto os{"windows"}; + auto os{"win"}; auto cpu_arch{"amd64"}; auto suitable_avx{"avx2"}; auto cuda_version{"12.4"}; @@ -123,7 +123,7 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { } { - auto os{"windows"}; + auto os{"win"}; auto cpu_arch{"amd64"}; auto suitable_avx{"avx2"}; auto cuda_version{"10"}; From 7c4d964f25a275dae68d62e267eb074505cf0f62 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 12:47:05 +0700 Subject: [PATCH 08/49] chore: cleanup --- engine/cli/command_line_parser.cc | 4 +--- engine/cli/commands/server_start_cmd.cc | 6 +----- engine/services/engine_service.cc | 22 +++++++++++++--------- engine/services/engine_service.h | 5 ++++- engine/services/hardware_service.cc | 7 +------ 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index dd10c7e3b..35ee26d36 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -51,9 +51,7 @@ CommandLineParser::CommandLineParser() download_service_{std::make_shared()}, dylib_path_manager_{std::make_shared()}, db_service_{std::make_shared()}, - engine_service_{std::make_shared( - download_service_, dylib_path_manager_, db_service_, - std::make_shared(1, "q"))} {} + engine_service_{std::make_shared(dylib_path_manager_)} {} bool CommandLineParser::SetupCommand(int argc, char** argv) { app_.usage("Usage:\n" + commands::GetCortexBinary() + diff --git a/engine/cli/commands/server_start_cmd.cc b/engine/cli/commands/server_start_cmd.cc index 693eef6c4..9544c70cf 100644 --- a/engine/cli/commands/server_start_cmd.cc +++ b/engine/cli/commands/server_start_cmd.cc @@ -106,11 +106,7 @@ bool ServerStartCmd::Exec(const std::string& host, int port, #else std::vector commands; // Some engines requires to add lib search path before process being created - auto download_srv = std::make_shared(); - auto dylib_path_mng = std::make_shared(); - auto db_srv = std::make_shared(); - EngineService(download_srv, dylib_path_mng, db_srv, - std::make_shared(1, "task_queue")) + EngineService(std::make_shared()) .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() + "/" + exe; diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index c45240828..a59bb3f53 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -301,21 +301,25 @@ cpp::result EngineService::DownloadEngine( meta << "name: " << variant.value() << std::endl; meta << "version: " << selected_variant->version << std::endl; meta.close(); - namespace fs = std::filesystem; - fs::path bin_path = extract_path / "build" / "bin"; - if (fs::exists(bin_path)) { - for (const auto& entry : fs::directory_iterator(bin_path)) { + std::filesystem::path bin_path = extract_path / "build" / "bin"; + if (std::filesystem::exists(bin_path)) { + for (const auto& entry : + std::filesystem::directory_iterator(bin_path)) { if (entry.is_regular_file()) { - fs::path target_file = extract_path / entry.path().filename(); - fs::copy_file(entry.path(), target_file, - fs::copy_options::overwrite_existing); + std::filesystem::path target_file = + extract_path / entry.path().filename(); + std::filesystem::copy_file( + entry.path(), target_file, + std::filesystem::copy_options::overwrite_existing); } } std::filesystem::remove_all(bin_path.parent_path()); } - if (!fs::exists(extract_path.parent_path().parent_path() / "deps")) { - fs::create_directory(extract_path.parent_path().parent_path() / "deps"); + if (!std::filesystem::exists(extract_path.parent_path().parent_path() / + "deps")) { + std::filesystem::create_directory( + extract_path.parent_path().parent_path() / "deps"); } std::filesystem::permissions(extract_path / kLlamaServer, std::filesystem::perms::owner_exec | diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index f470d00ef..9a907c127 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -64,7 +64,7 @@ class EngineService : public EngineServiceI { std::shared_ptr q_ = nullptr; public: - explicit EngineService( + EngineService( std::shared_ptr download_service, std::shared_ptr dylib_path_manager, std::shared_ptr db_service, @@ -77,6 +77,9 @@ class EngineService : public EngineServiceI { db_service_(db_service), q_(q) {} + EngineService(std::shared_ptr dylib_path_manager) + : dylib_path_manager_(dylib_path_manager) {} + std::vector GetEngineInfoList() const; /** diff --git a/engine/services/hardware_service.cc b/engine/services/hardware_service.cc index 5c4e694f5..626fba431 100644 --- a/engine/services/hardware_service.cc +++ b/engine/services/hardware_service.cc @@ -203,12 +203,7 @@ bool HardwareService::Restart(const std::string& host, int port) { #else std::vector commands; // Some engines requires to add lib search path before process being created - auto download_srv = std::make_shared(); - auto dylib_path_mng = std::make_shared(); - auto db_srv = std::make_shared(); - // TODO(sang) refactor this - EngineService(download_srv, dylib_path_mng, db_srv, - std::make_shared(1, "task_queue")) + EngineService(std::make_shared()) .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() / exe; commands.push_back(p); From 1e5beaf439550a71a47bb17e7d812c8c30adb00d Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 12:47:05 +0700 Subject: [PATCH 09/49] chore: cleanup --- engine/cli/command_line_parser.cc | 4 +--- engine/cli/commands/engine_install_cmd.cc | 4 ++-- engine/cli/commands/server_start_cmd.cc | 6 +----- engine/cli/main.cc | 2 +- engine/services/engine_service.cc | 22 +++++++++++++--------- engine/services/engine_service.h | 5 ++++- engine/services/hardware_service.cc | 7 +------ 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index dd10c7e3b..35ee26d36 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -51,9 +51,7 @@ CommandLineParser::CommandLineParser() download_service_{std::make_shared()}, dylib_path_manager_{std::make_shared()}, db_service_{std::make_shared()}, - engine_service_{std::make_shared( - download_service_, dylib_path_manager_, db_service_, - std::make_shared(1, "q"))} {} + engine_service_{std::make_shared(dylib_path_manager_)} {} bool CommandLineParser::SetupCommand(int argc, char** argv) { app_.usage("Usage:\n" + commands::GetCortexBinary() + diff --git a/engine/cli/commands/engine_install_cmd.cc b/engine/cli/commands/engine_install_cmd.cc index 79e524996..bda2bf2cc 100644 --- a/engine/cli/commands/engine_install_cmd.cc +++ b/engine/cli/commands/engine_install_cmd.cc @@ -92,8 +92,8 @@ bool EngineInstallCmd::Exec(const std::string& engine, auto v_name = variant["name"].asString(); if ((string_utils::StringContainsIgnoreCase(v_name, hw_inf_.sys_inf->os) || - (hw_inf_.sys_inf->os == "linux" && - string_utils::StringContainsIgnoreCase(v_name, "ubuntu"))) && + (hw_inf_.sys_inf->os == kLinuxOs && + string_utils::StringContainsIgnoreCase(v_name, kUbuntuOs))) && string_utils::StringContainsIgnoreCase(v_name, hw_inf_.sys_inf->arch)) { variant_selections.push_back(variant["name"].asString()); diff --git a/engine/cli/commands/server_start_cmd.cc b/engine/cli/commands/server_start_cmd.cc index 693eef6c4..9544c70cf 100644 --- a/engine/cli/commands/server_start_cmd.cc +++ b/engine/cli/commands/server_start_cmd.cc @@ -106,11 +106,7 @@ bool ServerStartCmd::Exec(const std::string& host, int port, #else std::vector commands; // Some engines requires to add lib search path before process being created - auto download_srv = std::make_shared(); - auto dylib_path_mng = std::make_shared(); - auto db_srv = std::make_shared(); - EngineService(download_srv, dylib_path_mng, db_srv, - std::make_shared(1, "task_queue")) + EngineService(std::make_shared()) .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() + "/" + exe; diff --git a/engine/cli/main.cc b/engine/cli/main.cc index f082436bc..fe6fdb39c 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -155,7 +155,7 @@ int main(int argc, char* argv[]) { auto get_latest_version = []() -> cpp::result { try { auto res = github_release_utils::GetReleaseByVersion( - kGgmlOrg, "llama.cpp", "latest"); + kGgmlOrg, kLlamaRepo, "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: " + diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index c45240828..a59bb3f53 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -301,21 +301,25 @@ cpp::result EngineService::DownloadEngine( meta << "name: " << variant.value() << std::endl; meta << "version: " << selected_variant->version << std::endl; meta.close(); - namespace fs = std::filesystem; - fs::path bin_path = extract_path / "build" / "bin"; - if (fs::exists(bin_path)) { - for (const auto& entry : fs::directory_iterator(bin_path)) { + std::filesystem::path bin_path = extract_path / "build" / "bin"; + if (std::filesystem::exists(bin_path)) { + for (const auto& entry : + std::filesystem::directory_iterator(bin_path)) { if (entry.is_regular_file()) { - fs::path target_file = extract_path / entry.path().filename(); - fs::copy_file(entry.path(), target_file, - fs::copy_options::overwrite_existing); + std::filesystem::path target_file = + extract_path / entry.path().filename(); + std::filesystem::copy_file( + entry.path(), target_file, + std::filesystem::copy_options::overwrite_existing); } } std::filesystem::remove_all(bin_path.parent_path()); } - if (!fs::exists(extract_path.parent_path().parent_path() / "deps")) { - fs::create_directory(extract_path.parent_path().parent_path() / "deps"); + if (!std::filesystem::exists(extract_path.parent_path().parent_path() / + "deps")) { + std::filesystem::create_directory( + extract_path.parent_path().parent_path() / "deps"); } std::filesystem::permissions(extract_path / kLlamaServer, std::filesystem::perms::owner_exec | diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index f470d00ef..9a907c127 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -64,7 +64,7 @@ class EngineService : public EngineServiceI { std::shared_ptr q_ = nullptr; public: - explicit EngineService( + EngineService( std::shared_ptr download_service, std::shared_ptr dylib_path_manager, std::shared_ptr db_service, @@ -77,6 +77,9 @@ class EngineService : public EngineServiceI { db_service_(db_service), q_(q) {} + EngineService(std::shared_ptr dylib_path_manager) + : dylib_path_manager_(dylib_path_manager) {} + std::vector GetEngineInfoList() const; /** diff --git a/engine/services/hardware_service.cc b/engine/services/hardware_service.cc index 5c4e694f5..626fba431 100644 --- a/engine/services/hardware_service.cc +++ b/engine/services/hardware_service.cc @@ -203,12 +203,7 @@ bool HardwareService::Restart(const std::string& host, int port) { #else std::vector commands; // Some engines requires to add lib search path before process being created - auto download_srv = std::make_shared(); - auto dylib_path_mng = std::make_shared(); - auto db_srv = std::make_shared(); - // TODO(sang) refactor this - EngineService(download_srv, dylib_path_mng, db_srv, - std::make_shared(1, "task_queue")) + EngineService(std::make_shared()) .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() / exe; commands.push_back(p); From b7b772bdad468942282af22733566ec5663212bf Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 14:35:30 +0700 Subject: [PATCH 10/49] fix: unit tests --- engine/extensions/local-engine/local_engine.h | 1 + engine/services/engine_service.h | 1 - .../components/test_engine_matcher_utils.cc | 156 ++++++------------ engine/utils/engine_matcher_utils.h | 15 +- 4 files changed, 61 insertions(+), 112 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h index 345181958..972a4482d 100644 --- a/engine/extensions/local-engine/local_engine.h +++ b/engine/extensions/local-engine/local_engine.h @@ -22,6 +22,7 @@ struct ServerAddress { std::string ai_prompt; std::string system_prompt; }; + class LocalEngine : public EngineI { public: LocalEngine(EngineService& engine_service, TaskQueue& q) diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index 9a907c127..cf41949b6 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -45,7 +45,6 @@ class EngineService : public EngineServiceI { using EngineVariant = github_release_utils::GitHubAsset; struct EngineInfo { - std::unique_ptr dl; EngineV engine; }; diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index 5329d6b8e..f0bf64b4d 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -6,95 +6,38 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { protected: const std::vector cortex_llamacpp_variants{ - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx2-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx2-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx2.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx512-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx512-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx512.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-noavx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-noavx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-noavx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-vulkan.tar.gz", - "cortex.llamacpp-0.1.43-linux-arm64.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-mac-amd64.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-mac-arm64.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx512-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx512-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx512.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-noavx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-noavx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-noavx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-vulkan.tar.gz", + "llama-b4920-bin-ubuntu-arm64.zip", + "llama-b4920-bin-linux-avx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-avx-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-linux-avx-x64.tar.gz", + "llama-b4920-bin-linux-avx2-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-avx2-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-ubuntu-x64.tar.gz", + "llama-b4920-bin-linux-avx512-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-avx512-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-linux-avx512-x64.tar.gz", + "llama-b4920-bin-linux-noavx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-noavx-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-linux-noavx-x64.tar.gz", + "llama-b4920-bin-ubuntu-vulkan-x64.tar.gz", + "llama-b4920-bin-macos-arm64.zip", + "llama-b4920-bin-macos-x64.zip", + "llama-b4920-bin-windows-amd64-avx-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-avx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx-x64.zip", + "llama-b4920-bin-windows-amd64-avx2-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx2-x64.zip", + "llama-b4920-bin-windows-amd64-avx512-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-avx512-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx512-x64.zip", + "llama-b4920-bin-windows-amd64-noavx-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-noavx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-noavx-x64.zip", + "llama-b4920-bin-win-vulkan-x64.zip", }; - - const std::vector cortex_tensorrt_variants{ - "cortex.tensorrt-llm-0.0.9-linux-cuda-12-4.tar.gz", - "cortex.tensorrt-llm-0.0.9-windows-cuda-12-4.tar.gz"}; - - const std::vector cortex_onnx_variants{ - "cortex.onnx-0.1.7-windows-amd64.tar.gz"}; }; -TEST_F(EngineMatcherUtilsTestSuite, TestValidateOnnx) { - - { - auto expect_matched_variant = cortex_onnx_variants[0]; - auto result = engine_matcher_utils::ValidateOnnx(cortex_onnx_variants, - "windows", "amd64"); - - EXPECT_EQ(result, expect_matched_variant); - } - - { - // should return an empty variant because no variant matched - auto expect_matched_variant{""}; - auto windows_arm_result = engine_matcher_utils::ValidateOnnx( - cortex_onnx_variants, "windows", "arm"); - auto mac_arm64_result = engine_matcher_utils::ValidateOnnx( - cortex_onnx_variants, "mac", "arm64"); - - EXPECT_EQ(windows_arm_result, expect_matched_variant); - EXPECT_EQ(mac_arm64_result, expect_matched_variant); - } -} - -TEST_F(EngineMatcherUtilsTestSuite, TestValidateTensorrt) { - - { - auto windows_expect_matched_variant{cortex_tensorrt_variants[1]}; - auto linux_expect_matched_variant{cortex_tensorrt_variants[0]}; - auto windows{"windows"}; - auto linux{"linux"}; - auto cuda_version{"12.4"}; - auto windows_result = engine_matcher_utils::ValidateTensorrtLlm( - cortex_tensorrt_variants, windows, cuda_version); - auto linux_result = engine_matcher_utils::ValidateTensorrtLlm( - cortex_tensorrt_variants, linux, cuda_version); - - EXPECT_EQ(windows_result, windows_expect_matched_variant); - EXPECT_EQ(linux_result, linux_expect_matched_variant); - } - - { // macos is not supported - auto os = "mac"; - auto cuda_version{"12.4"}; - - auto result = engine_matcher_utils::ValidateTensorrtLlm( - cortex_tensorrt_variants, os, cuda_version); - EXPECT_EQ(result, ""); - } -} - TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { { auto os{"win"}; @@ -105,26 +48,24 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ( - variant, - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2-cuda-12-0.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz"); } { auto os{"mac"}; - auto cpu_arch{"amd64"}; + auto cpu_arch{"x64"}; auto suitable_avx{""}; auto cuda_version{""}; auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ(variant, "cortex.llamacpp-0.1.25-25.08.24-mac-amd64.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-macos-x64.zip"); } { auto os{"win"}; - auto cpu_arch{"amd64"}; + auto cpu_arch{"x64"}; auto suitable_avx{"avx2"}; auto cuda_version{"10"}; @@ -132,8 +73,7 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); // fallback to no cuda version - EXPECT_EQ(variant, - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-win-avx2-x64.zip"); } { @@ -145,30 +85,36 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ(variant, "cortex.llamacpp-0.1.43-linux-arm64.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-ubuntu-arm64.zip"); } } TEST_F(EngineMatcherUtilsTestSuite, TestGetVersionAndArch) { { - std::string variant = - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx-cuda-11-7.tar.gz"; + std::string variant = "llama-b4920-bin-linux-avx-cuda-cu11.7-x64.tar.gz"; + auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "linux-avx-cuda-cu11.7-x64"); + } + + { + std::string variant = "llama-b4920-bin-ubuntu-arm64.zip"; auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); - EXPECT_EQ(version, "v0.1.25-25.08.24"); - EXPECT_EQ(arch, "linux-amd64-avx-cuda-11-7"); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "ubuntu-arm64"); } { - std::string variant = "cortex.llamacpp-0.1.25-windows-amd64-avx2.tar.gz"; + std::string variant = "llama-b4920-bin-win-avx2-x64.zip"; auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); - EXPECT_EQ(version, "v0.1.25"); - EXPECT_EQ(arch, "windows-amd64-avx2"); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "win-avx2-x64"); } { - std::string variant = "cortex.llamacpp-0.1.25-25.08.24-mac-amd64.tar.gz"; + std::string variant = "llama-b4920-bin-macos-x64.tar.gz"; auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); - EXPECT_EQ(version, "v0.1.25-25.08.24"); - EXPECT_EQ(arch, "mac-amd64"); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "macos-x64"); } } diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index f33b738ed..0689fd726 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -206,15 +206,18 @@ inline std::string Validate(const std::vector& variants, inline std::pair GetVersionAndArch( const std::string& file_name) { // Remove the file extension - std::string base = file_name.substr(0, file_name.find("tar") - 1); + std::string b = string_utils::RemoveSubstring(file_name, ".tar.gz"); + std::string base = string_utils::RemoveSubstring(b, ".zip"); size_t arch_pos = 0; - if (base.find("windows") != std::string::npos) { - arch_pos = base.find("-windows"); + if (base.find("win") != std::string::npos) { + arch_pos = base.find("-bin-win"); } else if (base.find("linux") != std::string::npos) { - arch_pos = base.find("-linux"); + arch_pos = base.find("-bin-linux"); + } else if (base.find("ubuntu") != std::string::npos) { + arch_pos = base.find("-bin-ubuntu"); } else { - arch_pos = base.find("-mac"); + arch_pos = base.find("-bin-macos"); } // Extract architecture part @@ -223,6 +226,6 @@ inline std::pair GetVersionAndArch( // Extract version part size_t v_pos = base.find_first_of('-'); auto version = base.substr(v_pos + 1, arch_pos - v_pos - 1); - return std::pair("v" + version, arch); + return std::pair(version, string_utils::RemoveSubstring(arch, "bin-")); } } // namespace engine_matcher_utils From 7c661357d28125bccc02cc523f935f9254586657 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 15:24:22 +0700 Subject: [PATCH 11/49] fix: e2e tests --- docs/static/openapi/cortex.json | 12 +++++------ .../e2e-test/api/engines/test_api_engine.py | 12 +++++------ .../test_api_engine_install_nightly.py | 4 ++-- .../engines/test_api_get_default_engine.py | 4 ++-- .../api/engines/test_api_get_list_engine.py | 4 ++-- .../engines/test_api_post_default_engine.py | 4 ++-- .../api/hardware/test_api_get_hardware.py | 20 ------------------- engine/e2e-test/api/model/test_api_model.py | 1 + .../cli/engines/test_cli_engine_install.py | 18 +---------------- .../runner/cortex-llamacpp-e2e-nightly.py | 2 +- engine/e2e-test/runner/main.py | 2 +- .../test_api_cortexso_hub_llamacpp_engine.py | 2 +- .../components/test_engine_matcher_utils.cc | 7 +++++++ .../components/test_github_release_utils.cc | 6 +++--- 14 files changed, 35 insertions(+), 63 deletions(-) diff --git a/docs/static/openapi/cortex.json b/docs/static/openapi/cortex.json index 23970ef51..b7d628094 100644 --- a/docs/static/openapi/cortex.json +++ b/docs/static/openapi/cortex.json @@ -2754,7 +2754,7 @@ }, "version": { "type": "string", - "example": "0.1.35-28.10.24" + "example": "b4920" } } } @@ -2763,11 +2763,11 @@ { "engine": "llama-cpp", "name": "mac-arm64", - "version": "0.1.35-28.10.24" + "version": "b4920" }, { "engine": "llama-cpp", - "name": "linux-amd64-avx", + "name": "linux-avx-x64", "version": "0.1.35-27.10.24" } ] @@ -2901,7 +2901,7 @@ "name": { "type": "string", "description": "The name of the variant, including OS, architecture, and capabilities", - "example": "linux-amd64-avx-cuda-11-7" + "example": "linux-avx-x64-cuda-11-7" }, "created_at": { "type": "string", @@ -2973,7 +2973,7 @@ }, "name": { "type": "string", - "example": "0.1.39-linux-amd64-avx-cuda-11-7" + "example": "llama-b4920-bin-linux-avx-cuda-cu11.7" }, "size": { "type": "integer", @@ -3250,7 +3250,7 @@ }, "version": { "type": "string", - "example": "0.1.35-28.10.24" + "example": "b4920" } } } diff --git a/engine/e2e-test/api/engines/test_api_engine.py b/engine/e2e-test/api/engines/test_api_engine.py index 7356ef904..97c0c8558 100644 --- a/engine/e2e-test/api/engines/test_api_engine.py +++ b/engine/e2e-test/api/engines/test_api_engine.py @@ -28,14 +28,14 @@ def test_engines_get_llamacpp_should_be_successful(self): # engines install def test_engines_install_llamacpp_specific_version_and_variant(self): - data = {"version": "v0.1.40-b4354", "variant": "linux-amd64-avx"} + data = {"version": "b4920", "variant": "linux-avx-x64"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) assert response.status_code == 200 def test_engines_install_llamacpp_specific_version_and_null_variant(self): - data = {"version": "v0.1.40-b4354"} + data = {"version": "b4920"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) @@ -55,14 +55,14 @@ async def test_engines_install_uninstall_llamacpp_should_be_successful(self): @pytest.mark.asyncio async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_failed(self): # install first - data = {"variant": "mac-arm64"} + data = {"variant": "macos-arm64"} install_response = requests.post( "http://127.0.0.1:3928/v1/engines/llama-cpp/install", json=data ) await wait_for_websocket_download_success_event(timeout=120) assert install_response.status_code == 200 - data = {"version": "v0.1.35"} + data = {"version": "b4920"} response = requests.delete( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) @@ -72,7 +72,7 @@ async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_fa @pytest.mark.asyncio async def test_engines_install_uninstall_llamacpp_with_variant_should_be_successful(self): # install first - data = {"variant": "mac-arm64"} + data = {"variant": "macos-arm64"} install_response = requests.post( "http://127.0.0.1:3928/v1/engines/llama-cpp/install", json=data ) @@ -85,7 +85,7 @@ async def test_engines_install_uninstall_llamacpp_with_variant_should_be_success def test_engines_install_uninstall_llamacpp_with_specific_variant_and_version_should_be_successful( self, ): - data = {"variant": "mac-arm64", "version": "v0.1.35"} + data = {"variant": "macos-arm64", "version": "b4920"} # install first install_response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data diff --git a/engine/e2e-test/api/engines/test_api_engine_install_nightly.py b/engine/e2e-test/api/engines/test_api_engine_install_nightly.py index e92afb14b..088cc2474 100644 --- a/engine/e2e-test/api/engines/test_api_engine_install_nightly.py +++ b/engine/e2e-test/api/engines/test_api_engine_install_nightly.py @@ -2,7 +2,7 @@ import requests from utils.test_runner import start_server, stop_server, get_latest_pre_release_tag -latest_pre_release_tag = get_latest_pre_release_tag("menloresearch", "cortex.llamacpp") +latest_pre_release_tag = get_latest_pre_release_tag("menloresearch", "llama.cpp") class TestApiEngineInstall: @@ -23,7 +23,7 @@ def test_engines_install_llamacpp_should_be_successful(self): assert response.status_code == 200 def test_engines_install_llamacpp_specific_version_and_variant(self): - data = {"version": latest_pre_release_tag, "variant": "linux-amd64-avx"} + data = {"version": latest_pre_release_tag, "variant": "linux-avx-x64"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) diff --git a/engine/e2e-test/api/engines/test_api_get_default_engine.py b/engine/e2e-test/api/engines/test_api_get_default_engine.py index 2dfc467a3..4aef1623b 100644 --- a/engine/e2e-test/api/engines/test_api_get_default_engine.py +++ b/engine/e2e-test/api/engines/test_api_get_default_engine.py @@ -24,8 +24,8 @@ def setup_and_teardown(self): def test_api_get_default_engine_successfully(self): # Data test engine= "llama-cpp" - name= "linux-amd64-avx" - version= "v0.1.35-27.10.24" + name= "linux-avx-x64" + version= "b4920" data = {"version": version, "variant": name} post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" diff --git a/engine/e2e-test/api/engines/test_api_get_list_engine.py b/engine/e2e-test/api/engines/test_api_get_list_engine.py index e6baa22a6..b4249cb01 100644 --- a/engine/e2e-test/api/engines/test_api_get_list_engine.py +++ b/engine/e2e-test/api/engines/test_api_get_list_engine.py @@ -24,8 +24,8 @@ def setup_and_teardown(self): def test_api_get_list_engines_successfully(self): # Data test engine= "llama-cpp" - name= "linux-amd64-avx" - version= "v0.1.35-27.10.24" + name= "linux-avx-x64" + version= "b4920" post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" response = requests.delete( diff --git a/engine/e2e-test/api/engines/test_api_post_default_engine.py b/engine/e2e-test/api/engines/test_api_post_default_engine.py index b2b4e4c48..24b29d503 100644 --- a/engine/e2e-test/api/engines/test_api_post_default_engine.py +++ b/engine/e2e-test/api/engines/test_api_post_default_engine.py @@ -23,8 +23,8 @@ def setup_and_teardown(self): def test_api_set_default_engine_successfully(self): # Data test engine= "llama-cpp" - name= "linux-amd64-avx" - version= "v0.1.35-27.10.24" + name= "linux-avx-x64" + version= "b4920" data = {"version": version, "variant": name} post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" diff --git a/engine/e2e-test/api/hardware/test_api_get_hardware.py b/engine/e2e-test/api/hardware/test_api_get_hardware.py index 59b15ac18..0efecdbdc 100644 --- a/engine/e2e-test/api/hardware/test_api_get_hardware.py +++ b/engine/e2e-test/api/hardware/test_api_get_hardware.py @@ -88,25 +88,6 @@ def test_api_get_hardware_successfully(self): "example": True, "description": "Indicates if the GPU is currently activated." }, - "additional_information": { - "type": "object", - "properties": { - "compute_cap": { - "type": "string", - "example": "8.6", - "description": "The compute capability of the GPU." - }, - "driver_version": { - "type": "string", - "example": "535.183", - "description": "The version of the installed driver." - } - }, - "required": [ - "compute_cap", - "driver_version" - ] - }, "free_vram": { "type": "integer", "example": 23983, @@ -140,7 +121,6 @@ def test_api_get_hardware_successfully(self): }, "required": [ "activated", - "additional_information", "free_vram", "id", "name", diff --git a/engine/e2e-test/api/model/test_api_model.py b/engine/e2e-test/api/model/test_api_model.py index bacf7e1b0..8677fffa0 100644 --- a/engine/e2e-test/api/model/test_api_model.py +++ b/engine/e2e-test/api/model/test_api_model.py @@ -95,6 +95,7 @@ async def test_models_start_stop_should_be_successful(self): time.sleep(30) print("Pull model") + requests.delete("http://localhost:3928/v1/models/tinyllama:1b") json_body = {"model": "tinyllama:1b"} response = requests.post("http://localhost:3928/v1/models/pull", json=json_body) assert response.status_code == 200, f"Failed to pull model: tinyllama:1b" diff --git a/engine/e2e-test/cli/engines/test_cli_engine_install.py b/engine/e2e-test/cli/engines/test_cli_engine_install.py index 370ebe3f3..673d66fa0 100644 --- a/engine/e2e-test/cli/engines/test_cli_engine_install.py +++ b/engine/e2e-test/cli/engines/test_cli_engine_install.py @@ -31,25 +31,9 @@ def test_engines_install_llamacpp_should_be_successfully(self): assert len(response.json()) > 0 assert exit_code == 0, f"Install engine failed with error: {error}" - @pytest.mark.skipif(reason="Ignore onnx-runtime test") - def test_engines_install_onnx_on_macos_should_be_failed(self): - exit_code, output, error = run( - "Install Engine", ["engines", "install", "onnxruntime"] - ) - assert "is not supported on" in output, "Should display error message" - assert exit_code == 0, f"Install engine failed with error: {error}" - - @pytest.mark.skipif(reason="Ignore tensorrt-llm test") - def test_engines_install_onnx_on_tensorrt_should_be_failed(self): - exit_code, output, error = run( - "Install Engine", ["engines", "install", "tensorrt-llm"] - ) - assert "is not supported on" in output, "Should display error message" - assert exit_code == 0, f"Install engine failed with error: {error}" - @pytest.mark.skipif(platform.system() == "Windows", reason="Progress bar log issue on Windows") def test_engines_install_pre_release_llamacpp(self): - engine_version = "v0.1.43" + engine_version = "b4920" exit_code, output, error = run( "Install Engine", ["engines", "install", "llama-cpp", "-v", engine_version], diff --git a/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py b/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py index 9fc296d60..ea3cae242 100644 --- a/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py +++ b/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py @@ -21,7 +21,7 @@ from api.engines.test_api_get_default_engine import TestApiDefaultEngine from api.engines.test_api_get_engine_release import TestApiEngineRelease from api.engines.test_api_get_engine_release_latest import TestApiEngineReleaseLatest -from test_api_post_default_engine import TestApiSetDefaultEngine +from api.engines.test_api_post_default_engine import TestApiSetDefaultEngine from api.model.test_api_model import TestApiModel from api.model.test_api_model_import import TestApiModelImport from api.files.test_api_create_file import TestApiCreateFile diff --git a/engine/e2e-test/runner/main.py b/engine/e2e-test/runner/main.py index 49bdc5131..8a98d0ca3 100644 --- a/engine/e2e-test/runner/main.py +++ b/engine/e2e-test/runner/main.py @@ -21,7 +21,7 @@ from api.engines.test_api_get_default_engine import TestApiDefaultEngine from api.engines.test_api_get_engine_release import TestApiEngineRelease from api.engines.test_api_get_engine_release_latest import TestApiEngineReleaseLatest -from test_api_post_default_engine import TestApiSetDefaultEngine +from api.engines.test_api_post_default_engine import TestApiSetDefaultEngine from api.model.test_api_model import TestApiModel from api.model.test_api_model_import import TestApiModelImport from api.files.test_api_create_file import TestApiCreateFile diff --git a/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py b/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py index 7a3c2e232..a22000d93 100644 --- a/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py +++ b/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py @@ -125,7 +125,7 @@ async def test_models_on_cortexso_hub(self, model_url): "Install Engine", ["engines", "install", "llama-cpp"], timeout=None, capture = False ) root = Path.home() - assert os.path.exists(root / "cortexcpp" / "engines" / "cortex.llamacpp" / "version.txt") + assert os.path.exists(root / "cortexcpp" / "engines" / "llama.cpp" / "version.txt") assert exit_code == 0, f"Install engine failed with error: {error}" # Start the model diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index f0bf64b4d..ac2f6274a 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -117,4 +117,11 @@ TEST_F(EngineMatcherUtilsTestSuite, TestGetVersionAndArch) { EXPECT_EQ(version, "b4920"); EXPECT_EQ(arch, "macos-x64"); } + + { + std::string variant = "llama-b4920-bin-ubuntu-vulkan-x64.zip"; + auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "ubuntu-vulkan-x64"); + } } diff --git a/engine/test/components/test_github_release_utils.cc b/engine/test/components/test_github_release_utils.cc index 753e7270c..20c14b187 100644 --- a/engine/test/components/test_github_release_utils.cc +++ b/engine/test/components/test_github_release_utils.cc @@ -4,16 +4,16 @@ class GitHubReleaseUtilsTest : public ::testing::Test {}; TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseByVersion) { - auto version{"v0.1.36"}; + auto version{"b4920"}; auto result = github_release_utils::GetReleaseByVersion( - kMenloOrg, "cortex.llamacpp", version); + kMenloOrg, "llama.cpp", version); ASSERT_TRUE(result.has_value()); ASSERT_EQ(result->tag_name, version); } TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseList) { - auto result = github_release_utils::GetReleases(kMenloOrg, "cortex.llamacpp"); + auto result = github_release_utils::GetReleases(kMenloOrg, "llama.cpp"); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->size() > 0); From 4705a1b555b251c36a9e04dea3aaee15e46bd911 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 15:56:46 +0700 Subject: [PATCH 12/49] chore: cleanup --- .../extensions/local-engine/local_engine.cc | 73 +++++++++---------- engine/services/engine_service.h | 14 ++-- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 8e34d926e..f6cbe5cdb 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -447,13 +447,10 @@ void LocalEngine::HandleEmbedding(std::shared_ptr json_body, if (server_map_.find(model_id) != server_map_.end()) { auto& s = server_map_[model_id]; auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "embeddings", - }, + /*.protocol*/ "http", + /*.host*/ s.host + ":" + std::to_string(s.port), + /*.pathParams*/ {"v1", "embeddings"}, + /* .queries = */ {}, }; auto response = curl_utils::SimplePostJson(url.ToFullPath(), @@ -495,9 +492,10 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto wait_for_server_up = [this](const std::string& model, const std::string& host, int port) { auto url = url_parser::Url{ - .protocol = "http", - .host = host + ":" + std::to_string(port), - .pathParams = {"health"}, + /*.protocol*/ "http", + /*.host*/ host + ":" + std::to_string(port), + /*.pathParams*/ {"health"}, + /*.queries*/ {}, }; while (server_map_.find(model) != server_map_.end()) { auto res = curl_utils::SimpleGet(url.ToFullPath()); @@ -519,6 +517,11 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, server_map_[model_id].host = "127.0.0.1"; server_map_[model_id].port = GenerateRandomInteger(39400, 39999); auto& s = server_map_[model_id]; + s.pre_prompt = json_body->get("pre_prompt", "").asString(); + s.user_prompt = json_body->get("user_prompt", "USER: ").asString(); + s.ai_prompt = json_body->get("ai_prompt", "ASSISTANT: ").asString(); + s.system_prompt = + json_body->get("system_prompt", "ASSISTANT's RULE: ").asString(); std::vector params = ConvertJsonToParamsVector(*json_body); params.push_back("--host"); params.push_back(s.host); @@ -696,18 +699,24 @@ void LocalEngine::HandleOpenAiChatCompletion( // llama.cpp server only supports n = 1 (*json_body)["n"] = 1; + auto url = url_parser::Url{ + /*.protocol*/ "http", + /*.host*/ s.host + ":" + std::to_string(s.port), + /*.pathParams*/ {"v1", "chat", "completions"}, + /*.queries*/ {}, + }; + if (is_stream) { - q_.RunInQueue([s, json_body, callback, model] { + q_.RunInQueue([s, json_body, callback, model, url = std::move(url)] { auto curl = curl_easy_init(); if (!curl) { CTL_WRN("Failed to initialize CURL"); return; } - auto url = "http://" + s.host + ":" + std::to_string(s.port) + - "/v1/chat/completions"; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, url.ToFullPath().c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); + CTL_INF(url.ToFullPath()); struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, "Content-Type: application/json"); @@ -754,16 +763,6 @@ void LocalEngine::HandleOpenAiChatCompletion( }); } else { - auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "chat", - "completions", - }, - }; Json::Value result; // multiple choices for (int i = 0; i < n; i++) { @@ -810,6 +809,8 @@ void LocalEngine::HandleOpenAiChatCompletion( } } +// (sang) duplicate code but it is easier to clean when +// llama-server upstream is fully OpenAI API Compatible void LocalEngine::HandleNonOpenAiChatCompletion( std::shared_ptr json_body, http_callback&& callback, const std::string& model) { @@ -881,17 +882,23 @@ void LocalEngine::HandleNonOpenAiChatCompletion( (*json_body)["n"] = 1; int n_probs = json_body->get("n_probs", 0).asInt(); + auto url = url_parser::Url{ + /*.protocol*/ "http", + /*.host*/ s.host + ":" + std::to_string(s.port), + /*.pathParams*/ {"v1", "completions"}, + /*.queries*/ {}, + }; + if (is_stream) { - q_.RunInQueue([s, json_body, callback, n_probs, model] { + q_.RunInQueue([s, json_body, callback, n_probs, model, + url = std::move(url)] { auto curl = curl_easy_init(); if (!curl) { CTL_WRN("Failed to initialize CURL"); return; } - auto url = - "http://" + s.host + ":" + std::to_string(s.port) + "/v1/completions"; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, url.ToFullPath().c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); struct curl_slist* headers = nullptr; @@ -939,15 +946,7 @@ void LocalEngine::HandleNonOpenAiChatCompletion( }); } else { - auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "completions", - }, - }; + Json::Value result; int prompt_tokens = 0; int predicted_tokens = 0; diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index cf41949b6..ccca94673 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -63,11 +63,10 @@ class EngineService : public EngineServiceI { std::shared_ptr q_ = nullptr; public: - EngineService( - std::shared_ptr download_service, - std::shared_ptr dylib_path_manager, - std::shared_ptr db_service, - std::shared_ptr q) + EngineService(std::shared_ptr download_service, + std::shared_ptr dylib_path_manager, + std::shared_ptr db_service, + std::shared_ptr q) : download_service_{download_service}, dylib_path_manager_{dylib_path_manager}, hw_inf_{.sys_inf = system_info_utils::GetSystemInfo(), @@ -77,7 +76,10 @@ class EngineService : public EngineServiceI { q_(q) {} EngineService(std::shared_ptr dylib_path_manager) - : dylib_path_manager_(dylib_path_manager) {} + : dylib_path_manager_(dylib_path_manager), + hw_inf_{.sys_inf = system_info_utils::GetSystemInfo(), + .cuda_driver_version = + system_info_utils::GetDriverAndCudaVersion().second} {} std::vector GetEngineInfoList() const; From 6454eca87886ab91d7be3b3e1628ae39a6bcbbf8 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 16:15:39 +0700 Subject: [PATCH 13/49] fix: e2e tests --- engine/e2e-test/api/engines/test_api_engine.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/e2e-test/api/engines/test_api_engine.py b/engine/e2e-test/api/engines/test_api_engine.py index 97c0c8558..34f277b60 100644 --- a/engine/e2e-test/api/engines/test_api_engine.py +++ b/engine/e2e-test/api/engines/test_api_engine.py @@ -55,7 +55,7 @@ async def test_engines_install_uninstall_llamacpp_should_be_successful(self): @pytest.mark.asyncio async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_failed(self): # install first - data = {"variant": "macos-arm64"} + data = {"variant": "linux-avx-x64"} install_response = requests.post( "http://127.0.0.1:3928/v1/engines/llama-cpp/install", json=data ) @@ -72,7 +72,7 @@ async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_fa @pytest.mark.asyncio async def test_engines_install_uninstall_llamacpp_with_variant_should_be_successful(self): # install first - data = {"variant": "macos-arm64"} + data = {"variant": "linux-avx-x64"} install_response = requests.post( "http://127.0.0.1:3928/v1/engines/llama-cpp/install", json=data ) @@ -85,7 +85,7 @@ async def test_engines_install_uninstall_llamacpp_with_variant_should_be_success def test_engines_install_uninstall_llamacpp_with_specific_variant_and_version_should_be_successful( self, ): - data = {"variant": "macos-arm64", "version": "b4920"} + data = {"variant": "linux-avx-x64", "version": "b4920"} # install first install_response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data From efe5a0829610c07efe9bcce1b9d97d6a7ebb52a3 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 17:14:16 +0700 Subject: [PATCH 14/49] fix: validation --- engine/services/engine_service.cc | 2 +- .../components/test_engine_matcher_utils.cc | 32 +++++++++++++------ engine/utils/engine_matcher_utils.h | 4 +-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index a59bb3f53..9dc395c22 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -565,7 +565,7 @@ EngineService::GetEngineVariants(const std::string& engine, if (system_info->arch == "arm64" && name.find("arm64") != std::string::npos) arch_match = true; - if (system_info->arch == "amd64" && + if (system_info->arch == "x64" && name.find("x64") != std::string::npos) arch_match = true; diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index ac2f6274a..2c24a9b6f 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -22,17 +22,17 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { "llama-b4920-bin-ubuntu-vulkan-x64.tar.gz", "llama-b4920-bin-macos-arm64.zip", "llama-b4920-bin-macos-x64.zip", - "llama-b4920-bin-windows-amd64-avx-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-avx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-avx-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-avx-x64.zip", - "llama-b4920-bin-windows-amd64-avx2-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx2-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-avx2-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-avx2-x64.zip", - "llama-b4920-bin-windows-amd64-avx512-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-avx512-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx512-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-avx512-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-avx512-x64.zip", - "llama-b4920-bin-windows-amd64-noavx-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-noavx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-noavx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-noavx-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-noavx-x64.zip", "llama-b4920-bin-win-vulkan-x64.zip", }; @@ -41,14 +41,14 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { { auto os{"win"}; - auto cpu_arch{"amd64"}; + auto cpu_arch{"x64"}; auto suitable_avx{"avx2"}; auto cuda_version{"12.4"}; auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ(variant, "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-win-avx2-cuda-cu12.0-x64.tar.gz"); } { @@ -63,6 +63,18 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { EXPECT_EQ(variant, "llama-b4920-bin-macos-x64.zip"); } + { + auto os{"mac"}; + auto cpu_arch{"arm64"}; + auto suitable_avx{""}; + auto cuda_version{""}; + + auto variant = engine_matcher_utils::Validate( + cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); + + EXPECT_EQ(variant, "llama-b4920-bin-macos-arm64.zip"); + } + { auto os{"win"}; auto cpu_arch{"x64"}; diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index 0689fd726..1afdd194c 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -55,7 +55,7 @@ inline std::string GetSuitableAvxVariant(cortex::cpuid::CpuInfo& cpu_info) { inline std::string GetSuitableCudaVariant( const std::vector& variants, const std::string& cuda_version) { - std::regex cuda_reg("cuda-(\\d+)-(\\d+)"); + std::regex cuda_reg("cuda-cu(\\d+).(\\d+)"); std::smatch match; int requested_major = 0; @@ -148,7 +148,7 @@ inline std::string Validate(const std::vector& variants, const std::string& os, const std::string& cpu_arch, const std::string& suitable_avx, const std::string& cuda_version) { - CTL_INF(os << " " << cpu_arch); + // CTL_INF(os << " " << cpu_arch); // Early return if the OS is not supported if (os != kMacOs && os != kWindowsOs && os != kLinuxOs) { return ""; From a2886d2e24d03e9aceade50bedddf9f87ab00d5e Mon Sep 17 00:00:00 2001 From: sangjanai Date: Thu, 20 Mar 2025 05:48:15 +0700 Subject: [PATCH 15/49] fix: change GH user agent --- engine/utils/engine_constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/utils/engine_constants.h b/engine/utils/engine_constants.h index ee935f38a..695afb4c5 100644 --- a/engine/utils/engine_constants.h +++ b/engine/utils/engine_constants.h @@ -16,7 +16,7 @@ constexpr const auto kGgmlOrg = "ggml-org"; constexpr auto static kHuggingFaceHost = "huggingface.co"; constexpr auto static kGitHubHost = "api.github.com"; constexpr auto static kCortexFolderName = "cortexcpp"; -constexpr auto static kDefaultGHUserAgent = "cortexcpp"; +constexpr auto static kDefaultGHUserAgent = kMenloOrg; constexpr auto static kWindowsOs = "win"; constexpr auto static kMacOs = "mac"; From cf1a2cab25188e1a7cb63877f66b4d0a3bc848f7 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 11:07:42 +0700 Subject: [PATCH 16/49] chore: cleanup --- engine/cli/command_line_parser.cc | 20 ++--- engine/cli/commands/cortex_upd_cmd.cc | 4 +- engine/cli/commands/cortex_upd_cmd.h | 4 +- engine/cli/commands/engine_install_cmd.cc | 4 +- engine/cli/commands/server_start_cmd.cc | 6 +- engine/cli/main.cc | 7 +- .../extensions/local-engine/local_engine.cc | 79 +++++++++--------- engine/services/engine_service.cc | 82 +++++++++---------- engine/services/engine_service.h | 15 ++-- engine/services/hardware_service.cc | 7 +- engine/services/inference_service.cc | 34 ++++---- .../components/test_github_release_utils.cc | 4 +- engine/test/components/test_string_utils.cc | 40 +++++++++ engine/utils/engine_constants.h | 8 +- engine/utils/engine_matcher_utils.h | 48 +++++++---- engine/utils/github_release_utils.h | 5 -- 16 files changed, 204 insertions(+), 163 deletions(-) diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index 6c9ea271c..35ee26d36 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -51,9 +51,7 @@ CommandLineParser::CommandLineParser() download_service_{std::make_shared()}, dylib_path_manager_{std::make_shared()}, db_service_{std::make_shared()}, - engine_service_{std::make_shared( - download_service_, dylib_path_manager_, db_service_, - std::make_shared(1, "q"))} {} + engine_service_{std::make_shared(dylib_path_manager_)} {} bool CommandLineParser::SetupCommand(int argc, char** argv) { app_.usage("Usage:\n" + commands::GetCortexBinary() + @@ -124,14 +122,14 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) { } } #endif - // auto config = file_manager_utils::GetCortexConfig(); - // if (!config.llamacppVersion.empty() && - // config.latestLlamacppRelease != config.llamacppVersion) { - // CLI_LOG( - // "\nNew llama.cpp version available: " << config.latestLlamacppRelease); - // CLI_LOG("To update, run: " << commands::GetCortexBinary() - // << " engines update llama-cpp"); - // } + auto config = file_manager_utils::GetCortexConfig(); + if (!config.llamacppVersion.empty() && + config.latestLlamacppRelease != config.llamacppVersion) { + CLI_LOG( + "\nNew llama.cpp version available: " << config.latestLlamacppRelease); + CLI_LOG("To update, run: " << commands::GetCortexBinary() + << " engines update llama-cpp"); + } return true; } diff --git a/engine/cli/commands/cortex_upd_cmd.cc b/engine/cli/commands/cortex_upd_cmd.cc index 6c8baa1a4..88660eb1b 100644 --- a/engine/cli/commands/cortex_upd_cmd.cc +++ b/engine/cli/commands/cortex_upd_cmd.cc @@ -515,10 +515,10 @@ bool CortexUpdCmd::GetLinuxInstallScript(const std::string& v, const std::string& channel) { std::vector path_list; if (channel == "nightly") { - path_list = {"menloresearch", "cortex.cpp", "dev", "engine", + path_list = {kMenloOrg, "cortex.cpp", "dev", "engine", "templates", "linux", "install.sh"}; } else { - path_list = {"menloresearch", "cortex.cpp", "main", "engine", + path_list = {kMenloOrg, "cortex.cpp", "main", "engine", "templates", "linux", "install.sh"}; } auto url_obj = url_parser::Url{ diff --git a/engine/cli/commands/cortex_upd_cmd.h b/engine/cli/commands/cortex_upd_cmd.h index 7f02839cf..fdee6cc49 100644 --- a/engine/cli/commands/cortex_upd_cmd.h +++ b/engine/cli/commands/cortex_upd_cmd.h @@ -79,9 +79,9 @@ inline std::vector GetReleasePath() { if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) { return {"cortex", "latest", "version.json"}; } else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) { - return {"repos", "menloresearch", "cortex.cpp", "releases"}; + return {"repos", kMenloOrg, "cortex.cpp", "releases"}; } else { - return {"repos", "menloresearch", "cortex.cpp", "releases", "latest"}; + return {"repos", kMenloOrg, "cortex.cpp", "releases", "latest"}; } } diff --git a/engine/cli/commands/engine_install_cmd.cc b/engine/cli/commands/engine_install_cmd.cc index 79e524996..bda2bf2cc 100644 --- a/engine/cli/commands/engine_install_cmd.cc +++ b/engine/cli/commands/engine_install_cmd.cc @@ -92,8 +92,8 @@ bool EngineInstallCmd::Exec(const std::string& engine, auto v_name = variant["name"].asString(); if ((string_utils::StringContainsIgnoreCase(v_name, hw_inf_.sys_inf->os) || - (hw_inf_.sys_inf->os == "linux" && - string_utils::StringContainsIgnoreCase(v_name, "ubuntu"))) && + (hw_inf_.sys_inf->os == kLinuxOs && + string_utils::StringContainsIgnoreCase(v_name, kUbuntuOs))) && string_utils::StringContainsIgnoreCase(v_name, hw_inf_.sys_inf->arch)) { variant_selections.push_back(variant["name"].asString()); diff --git a/engine/cli/commands/server_start_cmd.cc b/engine/cli/commands/server_start_cmd.cc index 693eef6c4..9544c70cf 100644 --- a/engine/cli/commands/server_start_cmd.cc +++ b/engine/cli/commands/server_start_cmd.cc @@ -106,11 +106,7 @@ bool ServerStartCmd::Exec(const std::string& host, int port, #else std::vector commands; // Some engines requires to add lib search path before process being created - auto download_srv = std::make_shared(); - auto dylib_path_mng = std::make_shared(); - auto db_srv = std::make_shared(); - EngineService(download_srv, dylib_path_mng, db_srv, - std::make_shared(1, "task_queue")) + EngineService(std::make_shared()) .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() + "/" + exe; diff --git a/engine/cli/main.cc b/engine/cli/main.cc index 6b37c6156..fe6fdb39c 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -148,14 +148,14 @@ int main(int argc, char* argv[]) { std::chrono::hours(24); should_check_for_latest_llamacpp_version = now > last_check; } - - if (false) { + + 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"); + kGgmlOrg, kLlamaRepo, "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: " + @@ -171,6 +171,7 @@ int main(int argc, char* argv[]) { }; auto res = get_latest_version(); + if (res.has_error()) { CTL_ERR("Failed to get latest llama.cpp version: " << res.error()); return; diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index f64db1b4d..f6cbe5cdb 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -447,13 +447,10 @@ void LocalEngine::HandleEmbedding(std::shared_ptr json_body, if (server_map_.find(model_id) != server_map_.end()) { auto& s = server_map_[model_id]; auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "embeddings", - }, + /*.protocol*/ "http", + /*.host*/ s.host + ":" + std::to_string(s.port), + /*.pathParams*/ {"v1", "embeddings"}, + /* .queries = */ {}, }; auto response = curl_utils::SimplePostJson(url.ToFullPath(), @@ -495,9 +492,10 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto wait_for_server_up = [this](const std::string& model, const std::string& host, int port) { auto url = url_parser::Url{ - .protocol = "http", - .host = host + ":" + std::to_string(port), - .pathParams = {"health"}, + /*.protocol*/ "http", + /*.host*/ host + ":" + std::to_string(port), + /*.pathParams*/ {"health"}, + /*.queries*/ {}, }; while (server_map_.find(model) != server_map_.end()) { auto res = curl_utils::SimpleGet(url.ToFullPath()); @@ -519,6 +517,11 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, server_map_[model_id].host = "127.0.0.1"; server_map_[model_id].port = GenerateRandomInteger(39400, 39999); auto& s = server_map_[model_id]; + s.pre_prompt = json_body->get("pre_prompt", "").asString(); + s.user_prompt = json_body->get("user_prompt", "USER: ").asString(); + s.ai_prompt = json_body->get("ai_prompt", "ASSISTANT: ").asString(); + s.system_prompt = + json_body->get("system_prompt", "ASSISTANT's RULE: ").asString(); std::vector params = ConvertJsonToParamsVector(*json_body); params.push_back("--host"); params.push_back(s.host); @@ -530,13 +533,13 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, std::vector v; v.reserve(params.size() + 1); - auto engine_dir = engine_service_.GetEngineDirPath("llama.cpp"); + auto engine_dir = engine_service_.GetEngineDirPath(kLlamaRepo); if (engine_dir.has_error()) { CTL_WRN(engine_dir.error()); server_map_.erase(model_id); return; } - auto exe = (engine_dir.value().first / "llama-server").string(); + auto exe = (engine_dir.value().first / kLlamaServer).string(); v.push_back(exe); v.insert(v.end(), params.begin(), params.end()); @@ -544,7 +547,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto log_path = (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); - CTL_INF("log: " << log_path); + CTL_DBG("log: " << log_path); auto result = cortex::process::SpawnProcess(v, log_path, log_path); if (result.has_error()) { CTL_ERR("Fail to spawn process. " << result.error()); @@ -696,18 +699,24 @@ void LocalEngine::HandleOpenAiChatCompletion( // llama.cpp server only supports n = 1 (*json_body)["n"] = 1; + auto url = url_parser::Url{ + /*.protocol*/ "http", + /*.host*/ s.host + ":" + std::to_string(s.port), + /*.pathParams*/ {"v1", "chat", "completions"}, + /*.queries*/ {}, + }; + if (is_stream) { - q_.RunInQueue([s, json_body, callback, model] { + q_.RunInQueue([s, json_body, callback, model, url = std::move(url)] { auto curl = curl_easy_init(); if (!curl) { CTL_WRN("Failed to initialize CURL"); return; } - auto url = "http://" + s.host + ":" + std::to_string(s.port) + - "/v1/chat/completions"; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, url.ToFullPath().c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); + CTL_INF(url.ToFullPath()); struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, "Content-Type: application/json"); @@ -754,16 +763,6 @@ void LocalEngine::HandleOpenAiChatCompletion( }); } else { - auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "chat", - "completions", - }, - }; Json::Value result; // multiple choices for (int i = 0; i < n; i++) { @@ -810,6 +809,8 @@ void LocalEngine::HandleOpenAiChatCompletion( } } +// (sang) duplicate code but it is easier to clean when +// llama-server upstream is fully OpenAI API Compatible void LocalEngine::HandleNonOpenAiChatCompletion( std::shared_ptr json_body, http_callback&& callback, const std::string& model) { @@ -881,17 +882,23 @@ void LocalEngine::HandleNonOpenAiChatCompletion( (*json_body)["n"] = 1; int n_probs = json_body->get("n_probs", 0).asInt(); + auto url = url_parser::Url{ + /*.protocol*/ "http", + /*.host*/ s.host + ":" + std::to_string(s.port), + /*.pathParams*/ {"v1", "completions"}, + /*.queries*/ {}, + }; + if (is_stream) { - q_.RunInQueue([s, json_body, callback, n_probs, model] { + q_.RunInQueue([s, json_body, callback, n_probs, model, + url = std::move(url)] { auto curl = curl_easy_init(); if (!curl) { CTL_WRN("Failed to initialize CURL"); return; } - auto url = - "http://" + s.host + ":" + std::to_string(s.port) + "/v1/completions"; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, url.ToFullPath().c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); struct curl_slist* headers = nullptr; @@ -939,15 +946,7 @@ void LocalEngine::HandleNonOpenAiChatCompletion( }); } else { - auto url = url_parser::Url{ - .protocol = "http", - .host = s.host + ":" + std::to_string(s.port), - .pathParams = - { - "v1", - "completions", - }, - }; + Json::Value result; int prompt_tokens = 0; int predicted_tokens = 0; diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index 592c72d72..a59bb3f53 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -47,13 +47,6 @@ std::string Repo2Engine(const std::string& r) { } return r; }; - -std::string GetEnginePath(std::string_view e) { - if (e == kLlamaRepo) { - return kLlamaLibPath; - } - return kLlamaLibPath; -}; } // namespace cpp::result EngineService::InstallEngineAsync( @@ -238,11 +231,10 @@ cpp::result EngineService::DownloadEngine( : normalized_version; std::unordered_set merged_variant_name = { "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + - ".tar.gz", + ".tar.gz", // menlo "llama-" + latest_version_semantic + "-bin-" + variant_name.value() + - ".zip"}; + ".zip"}; // ggml - // CTL_INF("merged_variant_name: " << merged_variant_name); for (const auto& asset : res.value()) { if (merged_variant_name.find(asset.name) != merged_variant_name.end()) { selected_variant = asset; @@ -279,23 +271,21 @@ cpp::result EngineService::DownloadEngine( } } - // auto normalize_version = "v" + selected_variant->version; - auto normalize_version = selected_variant->version; auto variant_folder_name = engine_matcher_utils::GetVariantFromNameAndVersion( selected_variant->name, engine, selected_variant->version); auto variant_folder_path = file_manager_utils::GetEnginesContainerPath() / engine / variant_folder_name.value() / - normalize_version; + selected_variant->version; auto variant_path = variant_folder_path / selected_variant->name; std::filesystem::create_directories(variant_folder_path); CTL_INF("variant_folder_path: " + variant_folder_path.string()); - auto on_finished = [this, engine, selected_variant, variant_folder_path, - normalize_version](const DownloadTask& finishedTask) { + auto on_finished = [this, engine, selected_variant, + variant_folder_path](const DownloadTask& finishedTask) { // try to unzip the downloaded file CTL_INF("Engine zip path: " << finishedTask.items[0].localPath.string()); - CTL_INF("Version: " + normalize_version); + CTL_INF("Version: " + selected_variant->version); auto extract_path = finishedTask.items[0].localPath.parent_path(); archive_utils::ExtractArchive(finishedTask.items[0].localPath.string(), @@ -303,30 +293,35 @@ cpp::result EngineService::DownloadEngine( CTL_INF("local path: " << finishedTask.items[0].localPath.string() << ", extract path: " << extract_path.string()); auto variant = engine_matcher_utils::GetVariantFromNameAndVersion( - selected_variant->name, engine, normalize_version); + selected_variant->name, engine, selected_variant->version); CTL_INF("Extracted variant: " + variant.value()); try { + // Create version file std::ofstream meta(extract_path / "version.txt", std::ios::out); meta << "name: " << variant.value() << std::endl; - meta << "version: " << normalize_version << std::endl; + meta << "version: " << selected_variant->version << std::endl; meta.close(); - namespace fs = std::filesystem; - fs::path bin_path = extract_path / "build" / "bin"; - if (fs::exists(bin_path)) { - for (const auto& entry : fs::directory_iterator(bin_path)) { + std::filesystem::path bin_path = extract_path / "build" / "bin"; + if (std::filesystem::exists(bin_path)) { + for (const auto& entry : + std::filesystem::directory_iterator(bin_path)) { if (entry.is_regular_file()) { - fs::path target_file = extract_path / entry.path().filename(); - fs::copy_file(entry.path(), target_file, - fs::copy_options::overwrite_existing); + std::filesystem::path target_file = + extract_path / entry.path().filename(); + std::filesystem::copy_file( + entry.path(), target_file, + std::filesystem::copy_options::overwrite_existing); } } std::filesystem::remove_all(bin_path.parent_path()); } - if (!fs::exists(extract_path.parent_path().parent_path() / "deps")) { - fs::create_directory(extract_path.parent_path().parent_path() / "deps"); + if (!std::filesystem::exists(extract_path.parent_path().parent_path() / + "deps")) { + std::filesystem::create_directory( + extract_path.parent_path().parent_path() / "deps"); } - std::filesystem::permissions(extract_path / "llama-server", + std::filesystem::permissions(extract_path / kLlamaServer, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec, @@ -337,17 +332,17 @@ cpp::result EngineService::DownloadEngine( } // set as default - - auto res = - SetDefaultEngineVariant(engine, normalize_version, variant.value()); + auto res = SetDefaultEngineVariant(engine, selected_variant->version, + variant.value()); if (res.has_error()) { CTL_ERR("Failed to set default engine variant: " << res.error()); } else { CTL_INF("Set default engine variant: " << res.value().variant); } - auto create_res = EngineService::UpsertEngine( - engine, // engine_name - kLocal, "", "", normalize_version, variant.value(), "Default", ""); + auto create_res = + EngineService::UpsertEngine(engine, // engine_name + kLocal, "", "", selected_variant->version, + variant.value(), "Default", ""); if (create_res.has_error()) { CTL_ERR("Failed to create engine entry: " << create_res->engine_name); @@ -358,7 +353,7 @@ cpp::result EngineService::DownloadEngine( for (const auto& entry : std::filesystem::directory_iterator( variant_folder_path.parent_path())) { if (entry.is_directory() && - entry.path().filename() != normalize_version) { + entry.path().filename() != selected_variant->version) { try { std::filesystem::remove_all(entry.path()); } catch (const std::exception& e) { @@ -472,8 +467,8 @@ std::string EngineService::GetMatchedVariant( cpp::result, std::string> EngineService::GetEngineReleases(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto ggml_org = github_release_utils::GetReleases("ggml-org", ne); - auto menlo = github_release_utils::GetReleases("menloresearch", ne); + auto ggml_org = github_release_utils::GetReleases(kGgmlOrg, ne); + auto menlo = github_release_utils::GetReleases(kMenloOrg, ne); if (ggml_org.has_error() && menlo.has_error()) { return cpp::fail(ggml_org.error()); } @@ -500,13 +495,13 @@ EngineService::GetEngineVariants(const std::string& engine, bool filter_compatible_only) const { auto ne = cortex::engine::NormalizeEngine(engine); auto engine_release_menlo = - github_release_utils::GetReleaseByVersion("menloresearch", ne, version); + github_release_utils::GetReleaseByVersion(kMenloOrg, ne, version); auto engine_release_ggml = - github_release_utils::GetReleaseByVersion("ggml-org", ne, version); + github_release_utils::GetReleaseByVersion(kGgmlOrg, ne, version); if (engine_release_menlo.has_error() && engine_release_ggml.has_error()) { return cpp::fail("Failed to get engine release: " + - engine_release_menlo.error()); + engine_release_menlo.error()); } if (engine_release_menlo.has_error()) { CTL_WRN("Failed to get engine release: " << engine_release_menlo.error()); @@ -835,8 +830,8 @@ EngineService::GetEngineDirPath(const std::string& engine_name) { CTL_DBG("user defined engine path: " << user_defined_engine_path); const std::filesystem::path engine_dir_path = [&] { if (user_defined_engine_path != nullptr) { - return std::filesystem::path(user_defined_engine_path) / - GetEnginePath(ne) / selected_engine_variant->variant / + return std::filesystem::path(user_defined_engine_path) / kLlamaLibPath / + selected_engine_variant->variant / selected_engine_variant->version; } else { return file_manager_utils::GetEnginesContainerPath() / ne / @@ -897,8 +892,7 @@ std::vector EngineService::GetLoadedEngines() { cpp::result EngineService::GetLatestEngineVersion(const std::string& engine) const { auto ne = cortex::engine::NormalizeEngine(engine); - auto res = - github_release_utils::GetReleaseByVersion("menloresearch", ne, "latest"); + auto res = github_release_utils::GetReleaseByVersion(kMenloOrg, ne, "latest"); if (res.has_error()) { return cpp::fail("Failed to fetch engine " + engine + " latest version!"); } diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index f470d00ef..2841aa329 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -64,11 +64,10 @@ class EngineService : public EngineServiceI { std::shared_ptr q_ = nullptr; public: - explicit EngineService( - std::shared_ptr download_service, - std::shared_ptr dylib_path_manager, - std::shared_ptr db_service, - std::shared_ptr q) + EngineService(std::shared_ptr download_service, + std::shared_ptr dylib_path_manager, + std::shared_ptr db_service, + std::shared_ptr q) : download_service_{download_service}, dylib_path_manager_{dylib_path_manager}, hw_inf_{.sys_inf = system_info_utils::GetSystemInfo(), @@ -77,6 +76,12 @@ class EngineService : public EngineServiceI { db_service_(db_service), q_(q) {} + EngineService(std::shared_ptr dylib_path_manager) + : dylib_path_manager_(dylib_path_manager), + hw_inf_{.sys_inf = system_info_utils::GetSystemInfo(), + .cuda_driver_version = + system_info_utils::GetDriverAndCudaVersion().second} {} + std::vector GetEngineInfoList() const; /** diff --git a/engine/services/hardware_service.cc b/engine/services/hardware_service.cc index 5c4e694f5..626fba431 100644 --- a/engine/services/hardware_service.cc +++ b/engine/services/hardware_service.cc @@ -203,12 +203,7 @@ bool HardwareService::Restart(const std::string& host, int port) { #else std::vector commands; // Some engines requires to add lib search path before process being created - auto download_srv = std::make_shared(); - auto dylib_path_mng = std::make_shared(); - auto db_srv = std::make_shared(); - // TODO(sang) refactor this - EngineService(download_srv, dylib_path_mng, db_srv, - std::make_shared(1, "task_queue")) + EngineService(std::make_shared()) .RegisterEngineLibPath(); std::string p = cortex_utils::GetCurrentPath() / exe; commands.push_back(p); diff --git a/engine/services/inference_service.cc b/engine/services/inference_service.cc index 2a3ab2ccc..939c91d85 100644 --- a/engine/services/inference_service.cc +++ b/engine/services/inference_service.cc @@ -17,23 +17,23 @@ cpp::result InferenceService::HandleChatCompletion( CTL_DBG("engine_type: " << engine_type); auto tool_choice = json_body->get("tool_choice", Json::Value::null); auto model_id = json_body->get("model", "").asString(); - // if (saved_models_.find(model_id) != saved_models_.end()) { - // // check if model is started, if not start it first - // Json::Value root; - // root["model"] = model_id; - // root["engine"] = engine_type; - // auto ir = GetModelStatus(std::make_shared(root)); - // auto status = std::get<0>(ir)["status_code"].asInt(); - // if (status != drogon::k200OK) { - // CTL_INF("Model is not loaded, start loading it: " << model_id); - // // For remote engine, we use the updated configuration - // if (engine_service_->IsRemoteEngine(engine_type)) { - // (void)model_service_.lock()->StartModel(model_id, {}, false); - // } else { - // (void)LoadModel(saved_models_.at(model_id)); - // } - // } - // } + if (saved_models_.find(model_id) != saved_models_.end()) { + // check if model is started, if not start it first + Json::Value root; + root["model"] = model_id; + root["engine"] = engine_type; + auto ir = GetModelStatus(std::make_shared(root)); + auto status = std::get<0>(ir)["status_code"].asInt(); + if (status != drogon::k200OK) { + CTL_INF("Model is not loaded, start loading it: " << model_id); + // For remote engine, we use the updated configuration + if (engine_service_->IsRemoteEngine(engine_type)) { + (void)model_service_.lock()->StartModel(model_id, {}, false); + } else { + (void)LoadModel(saved_models_.at(model_id)); + } + } + } CTL_DBG("engine_type: " << engine_type); auto engine_result = engine_service_->GetLoadedEngine(engine_type); diff --git a/engine/test/components/test_github_release_utils.cc b/engine/test/components/test_github_release_utils.cc index ae1e2c7c2..753e7270c 100644 --- a/engine/test/components/test_github_release_utils.cc +++ b/engine/test/components/test_github_release_utils.cc @@ -6,14 +6,14 @@ class GitHubReleaseUtilsTest : public ::testing::Test {}; TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseByVersion) { auto version{"v0.1.36"}; auto result = github_release_utils::GetReleaseByVersion( - "menloresearch", "cortex.llamacpp", version); + kMenloOrg, "cortex.llamacpp", version); ASSERT_TRUE(result.has_value()); ASSERT_EQ(result->tag_name, version); } TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseList) { - auto result = github_release_utils::GetReleases("menloresearch", "cortex.llamacpp"); + auto result = github_release_utils::GetReleases(kMenloOrg, "cortex.llamacpp"); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->size() > 0); diff --git a/engine/test/components/test_string_utils.cc b/engine/test/components/test_string_utils.cc index e396f0ed1..1693bb166 100644 --- a/engine/test/components/test_string_utils.cc +++ b/engine/test/components/test_string_utils.cc @@ -288,4 +288,44 @@ TEST_F(StringUtilsTestSuite, LargeInputPerformance) { EXPECT_EQ(RemoveSubstring(large_input, to_remove), ""); } +TEST(LTrimTest, EmptyString) { + std::string s = ""; + LTrim(s); + EXPECT_EQ(s, ""); +} + +TEST(LTrimTest, NoSpaces) { + std::string s = "HelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, LeadingSpaces) { + std::string s = " HelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} +TEST(LTrimTest, LeadingTabs) { + std::string s = "\t\tHelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, LeadingNewlines) { + std::string s = "\n\nHelloWorld"; + LTrim(s); + EXPECT_EQ(s, "HelloWorld"); +} + +TEST(LTrimTest, OnlySpaces) { + std::string s = " "; + LTrim(s); + EXPECT_EQ(s, ""); +} + +TEST(LTrimTest, MixedSpaces) { + std::string s = " \t\nHelloWorld "; + LTrim(s); + EXPECT_EQ(s, "HelloWorld "); +} diff --git a/engine/utils/engine_constants.h b/engine/utils/engine_constants.h index 0e91c388f..ee935f38a 100644 --- a/engine/utils/engine_constants.h +++ b/engine/utils/engine_constants.h @@ -5,9 +5,12 @@ constexpr const auto kLlamaEngine = "llama-cpp"; constexpr const auto kRemote = "remote"; constexpr const auto kLocal = "local"; - constexpr const auto kLlamaRepo = "llama.cpp"; -constexpr const auto kLlamaLibPath = "./engines/cortex.llamacpp"; +constexpr const auto kLlamaLibPath = "./engines/llama.cpp"; +constexpr const auto kLlamaServer = "llama-server"; + +constexpr const auto kMenloOrg = "menloresearch"; +constexpr const auto kGgmlOrg = "ggml-org"; // other constants constexpr auto static kHuggingFaceHost = "huggingface.co"; @@ -18,6 +21,7 @@ constexpr auto static kDefaultGHUserAgent = "cortexcpp"; constexpr auto static kWindowsOs = "win"; constexpr auto static kMacOs = "mac"; constexpr auto static kLinuxOs = "linux"; +constexpr auto static kUbuntuOs = "ubuntu"; constexpr auto static kUnsupportedOs = "Unsupported OS"; constexpr auto static kCurlGetTimeout = 10; diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index 7c99f4a0e..f33b738ed 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -7,6 +7,7 @@ #include #include #include "utils/cpuid/cpu_info.h" +#include "utils/engine_constants.h" #include "utils/logging_utils.h" #include "utils/result.hpp" #include "utils/string_utils.h" @@ -24,19 +25,18 @@ inline cpp::result GetVariantFromNameAndVersion( if (engine.empty()) { return cpp::fail("Engine name is empty"); } - CTL_INF("version: " << version); - auto nv = string_utils::RemoveSubstring(version, "v"); - CTL_INF("nv: " << nv); - using namespace string_utils; - CTL_INF("engine_file_name: " << engine_file_name); - auto removed_extension = RemoveSubstring(engine_file_name, ".tar.gz"); - auto removed_extension_e = RemoveSubstring(removed_extension, ".zip"); - CTL_INF("removed_extension: " << removed_extension_e); - auto version_and_variant = RemoveSubstring(removed_extension_e, engine + "-"); - CTL_INF("version_and_variant: " << version_and_variant); - auto variant = RemoveSubstring(version_and_variant, nv + "-"); - auto v = RemoveSubstring(variant, "llama-bin-"); - CTL_INF("variant: " << v); + CTL_DBG("version: " << version); + namespace su = string_utils; + CTL_DBG("engine_file_name: " << engine_file_name); + auto rm_extension_menlo = su::RemoveSubstring(engine_file_name, ".tar.gz"); + auto rm_extension_ggml = su::RemoveSubstring(rm_extension_menlo, ".zip"); + CTL_DBG("removed_extension: " << rm_extension_ggml); + auto version_and_variant = + su::RemoveSubstring(rm_extension_ggml, engine + "-"); + CTL_DBG("version_and_variant: " << version_and_variant); + auto variant = su::RemoveSubstring(version_and_variant, version + "-"); + auto v = su::RemoveSubstring(variant, "llama-bin-"); + CTL_DBG("variant: " << v); return v; } @@ -148,8 +148,9 @@ inline std::string Validate(const std::vector& variants, const std::string& os, const std::string& cpu_arch, const std::string& suitable_avx, const std::string& cuda_version) { + CTL_INF(os << " " << cpu_arch); // Early return if the OS is not supported - if (os != "mac" && os != "windows" && os != "linux") { + if (os != kMacOs && os != kWindowsOs && os != kLinuxOs) { return ""; } @@ -157,6 +158,12 @@ inline std::string Validate(const std::vector& variants, std::copy_if(variants.begin(), variants.end(), std::back_inserter(os_and_arch_compatible_list), [&os, &cpu_arch](const std::string& variant) { + // In case of Linux, we need to include ubuntu version also + if (os == kLinuxOs) { + if (variant.find(kUbuntuOs) != std::string::npos && + variant.find(cpu_arch) != std::string::npos) + return true; + } auto os_match = "-" + os; auto cpu_arch_match = "-" + cpu_arch; @@ -164,10 +171,10 @@ inline std::string Validate(const std::vector& variants, variant.find(cpu_arch_match) != std::string::npos; }); - if (os == "mac" && !os_and_arch_compatible_list.empty()) + if (os == kMacOs && !os_and_arch_compatible_list.empty()) return os_and_arch_compatible_list[0]; - if (os == "linux" && cpu_arch == "arm64" && + if (os == kLinuxOs && cpu_arch == "arm64" && !os_and_arch_compatible_list.empty()) { return os_and_arch_compatible_list[0]; } @@ -177,7 +184,14 @@ inline std::string Validate(const std::vector& variants, std::copy_if(os_and_arch_compatible_list.begin(), os_and_arch_compatible_list.end(), std::back_inserter(avx_compatible_list), - [&suitable_avx](const std::string& variant) { + [&os, &cpu_arch, &suitable_avx](const std::string& variant) { + if (os == kLinuxOs && + (suitable_avx == "avx2" || suitable_avx == "avx512" || + cpu_arch == "arm64")) { + if (variant.find(std::string(kUbuntuOs) + "-" + cpu_arch) != + std::string::npos) + return true; + } auto suitable_avx_match = "-" + suitable_avx; return variant.find(suitable_avx_match) != std::string::npos; diff --git a/engine/utils/github_release_utils.h b/engine/utils/github_release_utils.h index ea07d3f05..a045a7437 100644 --- a/engine/utils/github_release_utils.h +++ b/engine/utils/github_release_utils.h @@ -179,11 +179,6 @@ inline cpp::result GetReleaseByVersion( std::vector path_params{"repos", author, repo, "releases"}; if (tag != "latest") { path_params.push_back("tags"); - - // if (!string_utils::StartsWith(tag, "v")) { - // path_params.push_back("v" + tag); - // } - path_params.push_back(tag); } else { path_params.push_back("latest"); From 282aae3fbc1dd1a31c07829b5d98625dc08ce682 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 12:34:53 +0700 Subject: [PATCH 17/49] chore: fix unit tests --- engine/extensions/local-engine/local_engine.h | 1 + engine/services/engine_service.h | 1 - .../components/test_engine_matcher_utils.cc | 160 ++++++------------ engine/utils/engine_matcher_utils.h | 15 +- 4 files changed, 63 insertions(+), 114 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h index 345181958..972a4482d 100644 --- a/engine/extensions/local-engine/local_engine.h +++ b/engine/extensions/local-engine/local_engine.h @@ -22,6 +22,7 @@ struct ServerAddress { std::string ai_prompt; std::string system_prompt; }; + class LocalEngine : public EngineI { public: LocalEngine(EngineService& engine_service, TaskQueue& q) diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h index 2841aa329..ccca94673 100644 --- a/engine/services/engine_service.h +++ b/engine/services/engine_service.h @@ -45,7 +45,6 @@ class EngineService : public EngineServiceI { using EngineVariant = github_release_utils::GitHubAsset; struct EngineInfo { - std::unique_ptr dl; EngineV engine; }; diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index 1d1ed47a8..f0bf64b4d 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -6,98 +6,41 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { protected: const std::vector cortex_llamacpp_variants{ - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx2-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx2-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx2.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx512-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx512-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx512.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-noavx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-noavx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-noavx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-vulkan.tar.gz", - "cortex.llamacpp-0.1.43-linux-arm64.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-mac-amd64.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-mac-arm64.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx512-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx512-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx512.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-noavx-cuda-11-7.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-noavx-cuda-12-0.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-noavx.tar.gz", - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-vulkan.tar.gz", + "llama-b4920-bin-ubuntu-arm64.zip", + "llama-b4920-bin-linux-avx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-avx-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-linux-avx-x64.tar.gz", + "llama-b4920-bin-linux-avx2-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-avx2-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-ubuntu-x64.tar.gz", + "llama-b4920-bin-linux-avx512-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-avx512-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-linux-avx512-x64.tar.gz", + "llama-b4920-bin-linux-noavx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-linux-noavx-cuda-cu12.0-x64.tar.gz", + "llama-b4920-bin-linux-noavx-x64.tar.gz", + "llama-b4920-bin-ubuntu-vulkan-x64.tar.gz", + "llama-b4920-bin-macos-arm64.zip", + "llama-b4920-bin-macos-x64.zip", + "llama-b4920-bin-windows-amd64-avx-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-avx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx-x64.zip", + "llama-b4920-bin-windows-amd64-avx2-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx2-x64.zip", + "llama-b4920-bin-windows-amd64-avx512-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-avx512-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx512-x64.zip", + "llama-b4920-bin-windows-amd64-noavx-cuda-11-7.tar.gz", + "llama-b4920-bin-windows-amd64-noavx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-noavx-x64.zip", + "llama-b4920-bin-win-vulkan-x64.zip", }; - - const std::vector cortex_tensorrt_variants{ - "cortex.tensorrt-llm-0.0.9-linux-cuda-12-4.tar.gz", - "cortex.tensorrt-llm-0.0.9-windows-cuda-12-4.tar.gz"}; - - const std::vector cortex_onnx_variants{ - "cortex.onnx-0.1.7-windows-amd64.tar.gz"}; }; -TEST_F(EngineMatcherUtilsTestSuite, TestValidateOnnx) { - - { - auto expect_matched_variant = cortex_onnx_variants[0]; - auto result = engine_matcher_utils::ValidateOnnx(cortex_onnx_variants, - "windows", "amd64"); - - EXPECT_EQ(result, expect_matched_variant); - } - - { - // should return an empty variant because no variant matched - auto expect_matched_variant{""}; - auto windows_arm_result = engine_matcher_utils::ValidateOnnx( - cortex_onnx_variants, "windows", "arm"); - auto mac_arm64_result = engine_matcher_utils::ValidateOnnx( - cortex_onnx_variants, "mac", "arm64"); - - EXPECT_EQ(windows_arm_result, expect_matched_variant); - EXPECT_EQ(mac_arm64_result, expect_matched_variant); - } -} - -TEST_F(EngineMatcherUtilsTestSuite, TestValidateTensorrt) { - - { - auto windows_expect_matched_variant{cortex_tensorrt_variants[1]}; - auto linux_expect_matched_variant{cortex_tensorrt_variants[0]}; - auto windows{"windows"}; - auto linux{"linux"}; - auto cuda_version{"12.4"}; - auto windows_result = engine_matcher_utils::ValidateTensorrtLlm( - cortex_tensorrt_variants, windows, cuda_version); - auto linux_result = engine_matcher_utils::ValidateTensorrtLlm( - cortex_tensorrt_variants, linux, cuda_version); - - EXPECT_EQ(windows_result, windows_expect_matched_variant); - EXPECT_EQ(linux_result, linux_expect_matched_variant); - } - - { // macos is not supported - auto os = "mac"; - auto cuda_version{"12.4"}; - - auto result = engine_matcher_utils::ValidateTensorrtLlm( - cortex_tensorrt_variants, os, cuda_version); - EXPECT_EQ(result, ""); - } -} - TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { { - auto os{"windows"}; + auto os{"win"}; auto cpu_arch{"amd64"}; auto suitable_avx{"avx2"}; auto cuda_version{"12.4"}; @@ -105,26 +48,24 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ( - variant, - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2-cuda-12-0.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz"); } { auto os{"mac"}; - auto cpu_arch{"amd64"}; + auto cpu_arch{"x64"}; auto suitable_avx{""}; auto cuda_version{""}; auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ(variant, "cortex.llamacpp-0.1.25-25.08.24-mac-amd64.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-macos-x64.zip"); } { - auto os{"windows"}; - auto cpu_arch{"amd64"}; + auto os{"win"}; + auto cpu_arch{"x64"}; auto suitable_avx{"avx2"}; auto cuda_version{"10"}; @@ -132,8 +73,7 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); // fallback to no cuda version - EXPECT_EQ(variant, - "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-win-avx2-x64.zip"); } { @@ -145,30 +85,36 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ(variant, "cortex.llamacpp-0.1.43-linux-arm64.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-ubuntu-arm64.zip"); } } TEST_F(EngineMatcherUtilsTestSuite, TestGetVersionAndArch) { { - std::string variant = - "cortex.llamacpp-0.1.25-25.08.24-linux-amd64-avx-cuda-11-7.tar.gz"; + std::string variant = "llama-b4920-bin-linux-avx-cuda-cu11.7-x64.tar.gz"; + auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "linux-avx-cuda-cu11.7-x64"); + } + + { + std::string variant = "llama-b4920-bin-ubuntu-arm64.zip"; auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); - EXPECT_EQ(version, "v0.1.25-25.08.24"); - EXPECT_EQ(arch, "linux-amd64-avx-cuda-11-7"); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "ubuntu-arm64"); } { - std::string variant = "cortex.llamacpp-0.1.25-windows-amd64-avx2.tar.gz"; + std::string variant = "llama-b4920-bin-win-avx2-x64.zip"; auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); - EXPECT_EQ(version, "v0.1.25"); - EXPECT_EQ(arch, "windows-amd64-avx2"); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "win-avx2-x64"); } { - std::string variant = "cortex.llamacpp-0.1.25-25.08.24-mac-amd64.tar.gz"; + std::string variant = "llama-b4920-bin-macos-x64.tar.gz"; auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); - EXPECT_EQ(version, "v0.1.25-25.08.24"); - EXPECT_EQ(arch, "mac-amd64"); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "macos-x64"); } } diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index f33b738ed..0689fd726 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -206,15 +206,18 @@ inline std::string Validate(const std::vector& variants, inline std::pair GetVersionAndArch( const std::string& file_name) { // Remove the file extension - std::string base = file_name.substr(0, file_name.find("tar") - 1); + std::string b = string_utils::RemoveSubstring(file_name, ".tar.gz"); + std::string base = string_utils::RemoveSubstring(b, ".zip"); size_t arch_pos = 0; - if (base.find("windows") != std::string::npos) { - arch_pos = base.find("-windows"); + if (base.find("win") != std::string::npos) { + arch_pos = base.find("-bin-win"); } else if (base.find("linux") != std::string::npos) { - arch_pos = base.find("-linux"); + arch_pos = base.find("-bin-linux"); + } else if (base.find("ubuntu") != std::string::npos) { + arch_pos = base.find("-bin-ubuntu"); } else { - arch_pos = base.find("-mac"); + arch_pos = base.find("-bin-macos"); } // Extract architecture part @@ -223,6 +226,6 @@ inline std::pair GetVersionAndArch( // Extract version part size_t v_pos = base.find_first_of('-'); auto version = base.substr(v_pos + 1, arch_pos - v_pos - 1); - return std::pair("v" + version, arch); + return std::pair(version, string_utils::RemoveSubstring(arch, "bin-")); } } // namespace engine_matcher_utils From e3dfea3202a6bd429756ec360ed1ecb038681e4e Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 15:24:22 +0700 Subject: [PATCH 18/49] fix: e2e tests --- docs/static/openapi/cortex.json | 12 +++++------ .../e2e-test/api/engines/test_api_engine.py | 12 +++++------ .../test_api_engine_install_nightly.py | 4 ++-- .../engines/test_api_get_default_engine.py | 4 ++-- .../api/engines/test_api_get_list_engine.py | 4 ++-- .../engines/test_api_post_default_engine.py | 4 ++-- .../api/hardware/test_api_get_hardware.py | 20 ------------------- engine/e2e-test/api/model/test_api_model.py | 1 + .../cli/engines/test_cli_engine_install.py | 18 +---------------- .../runner/cortex-llamacpp-e2e-nightly.py | 2 +- engine/e2e-test/runner/main.py | 2 +- .../test_api_cortexso_hub_llamacpp_engine.py | 2 +- .../components/test_engine_matcher_utils.cc | 7 +++++++ .../components/test_github_release_utils.cc | 6 +++--- 14 files changed, 35 insertions(+), 63 deletions(-) diff --git a/docs/static/openapi/cortex.json b/docs/static/openapi/cortex.json index 23970ef51..b7d628094 100644 --- a/docs/static/openapi/cortex.json +++ b/docs/static/openapi/cortex.json @@ -2754,7 +2754,7 @@ }, "version": { "type": "string", - "example": "0.1.35-28.10.24" + "example": "b4920" } } } @@ -2763,11 +2763,11 @@ { "engine": "llama-cpp", "name": "mac-arm64", - "version": "0.1.35-28.10.24" + "version": "b4920" }, { "engine": "llama-cpp", - "name": "linux-amd64-avx", + "name": "linux-avx-x64", "version": "0.1.35-27.10.24" } ] @@ -2901,7 +2901,7 @@ "name": { "type": "string", "description": "The name of the variant, including OS, architecture, and capabilities", - "example": "linux-amd64-avx-cuda-11-7" + "example": "linux-avx-x64-cuda-11-7" }, "created_at": { "type": "string", @@ -2973,7 +2973,7 @@ }, "name": { "type": "string", - "example": "0.1.39-linux-amd64-avx-cuda-11-7" + "example": "llama-b4920-bin-linux-avx-cuda-cu11.7" }, "size": { "type": "integer", @@ -3250,7 +3250,7 @@ }, "version": { "type": "string", - "example": "0.1.35-28.10.24" + "example": "b4920" } } } diff --git a/engine/e2e-test/api/engines/test_api_engine.py b/engine/e2e-test/api/engines/test_api_engine.py index 7356ef904..34f277b60 100644 --- a/engine/e2e-test/api/engines/test_api_engine.py +++ b/engine/e2e-test/api/engines/test_api_engine.py @@ -28,14 +28,14 @@ def test_engines_get_llamacpp_should_be_successful(self): # engines install def test_engines_install_llamacpp_specific_version_and_variant(self): - data = {"version": "v0.1.40-b4354", "variant": "linux-amd64-avx"} + data = {"version": "b4920", "variant": "linux-avx-x64"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) assert response.status_code == 200 def test_engines_install_llamacpp_specific_version_and_null_variant(self): - data = {"version": "v0.1.40-b4354"} + data = {"version": "b4920"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) @@ -55,14 +55,14 @@ async def test_engines_install_uninstall_llamacpp_should_be_successful(self): @pytest.mark.asyncio async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_failed(self): # install first - data = {"variant": "mac-arm64"} + data = {"variant": "linux-avx-x64"} install_response = requests.post( "http://127.0.0.1:3928/v1/engines/llama-cpp/install", json=data ) await wait_for_websocket_download_success_event(timeout=120) assert install_response.status_code == 200 - data = {"version": "v0.1.35"} + data = {"version": "b4920"} response = requests.delete( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) @@ -72,7 +72,7 @@ async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_fa @pytest.mark.asyncio async def test_engines_install_uninstall_llamacpp_with_variant_should_be_successful(self): # install first - data = {"variant": "mac-arm64"} + data = {"variant": "linux-avx-x64"} install_response = requests.post( "http://127.0.0.1:3928/v1/engines/llama-cpp/install", json=data ) @@ -85,7 +85,7 @@ async def test_engines_install_uninstall_llamacpp_with_variant_should_be_success def test_engines_install_uninstall_llamacpp_with_specific_variant_and_version_should_be_successful( self, ): - data = {"variant": "mac-arm64", "version": "v0.1.35"} + data = {"variant": "linux-avx-x64", "version": "b4920"} # install first install_response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data diff --git a/engine/e2e-test/api/engines/test_api_engine_install_nightly.py b/engine/e2e-test/api/engines/test_api_engine_install_nightly.py index e92afb14b..088cc2474 100644 --- a/engine/e2e-test/api/engines/test_api_engine_install_nightly.py +++ b/engine/e2e-test/api/engines/test_api_engine_install_nightly.py @@ -2,7 +2,7 @@ import requests from utils.test_runner import start_server, stop_server, get_latest_pre_release_tag -latest_pre_release_tag = get_latest_pre_release_tag("menloresearch", "cortex.llamacpp") +latest_pre_release_tag = get_latest_pre_release_tag("menloresearch", "llama.cpp") class TestApiEngineInstall: @@ -23,7 +23,7 @@ def test_engines_install_llamacpp_should_be_successful(self): assert response.status_code == 200 def test_engines_install_llamacpp_specific_version_and_variant(self): - data = {"version": latest_pre_release_tag, "variant": "linux-amd64-avx"} + data = {"version": latest_pre_release_tag, "variant": "linux-avx-x64"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) diff --git a/engine/e2e-test/api/engines/test_api_get_default_engine.py b/engine/e2e-test/api/engines/test_api_get_default_engine.py index 2dfc467a3..4aef1623b 100644 --- a/engine/e2e-test/api/engines/test_api_get_default_engine.py +++ b/engine/e2e-test/api/engines/test_api_get_default_engine.py @@ -24,8 +24,8 @@ def setup_and_teardown(self): def test_api_get_default_engine_successfully(self): # Data test engine= "llama-cpp" - name= "linux-amd64-avx" - version= "v0.1.35-27.10.24" + name= "linux-avx-x64" + version= "b4920" data = {"version": version, "variant": name} post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" diff --git a/engine/e2e-test/api/engines/test_api_get_list_engine.py b/engine/e2e-test/api/engines/test_api_get_list_engine.py index e6baa22a6..b4249cb01 100644 --- a/engine/e2e-test/api/engines/test_api_get_list_engine.py +++ b/engine/e2e-test/api/engines/test_api_get_list_engine.py @@ -24,8 +24,8 @@ def setup_and_teardown(self): def test_api_get_list_engines_successfully(self): # Data test engine= "llama-cpp" - name= "linux-amd64-avx" - version= "v0.1.35-27.10.24" + name= "linux-avx-x64" + version= "b4920" post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" response = requests.delete( diff --git a/engine/e2e-test/api/engines/test_api_post_default_engine.py b/engine/e2e-test/api/engines/test_api_post_default_engine.py index b2b4e4c48..24b29d503 100644 --- a/engine/e2e-test/api/engines/test_api_post_default_engine.py +++ b/engine/e2e-test/api/engines/test_api_post_default_engine.py @@ -23,8 +23,8 @@ def setup_and_teardown(self): def test_api_set_default_engine_successfully(self): # Data test engine= "llama-cpp" - name= "linux-amd64-avx" - version= "v0.1.35-27.10.24" + name= "linux-avx-x64" + version= "b4920" data = {"version": version, "variant": name} post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" diff --git a/engine/e2e-test/api/hardware/test_api_get_hardware.py b/engine/e2e-test/api/hardware/test_api_get_hardware.py index 59b15ac18..0efecdbdc 100644 --- a/engine/e2e-test/api/hardware/test_api_get_hardware.py +++ b/engine/e2e-test/api/hardware/test_api_get_hardware.py @@ -88,25 +88,6 @@ def test_api_get_hardware_successfully(self): "example": True, "description": "Indicates if the GPU is currently activated." }, - "additional_information": { - "type": "object", - "properties": { - "compute_cap": { - "type": "string", - "example": "8.6", - "description": "The compute capability of the GPU." - }, - "driver_version": { - "type": "string", - "example": "535.183", - "description": "The version of the installed driver." - } - }, - "required": [ - "compute_cap", - "driver_version" - ] - }, "free_vram": { "type": "integer", "example": 23983, @@ -140,7 +121,6 @@ def test_api_get_hardware_successfully(self): }, "required": [ "activated", - "additional_information", "free_vram", "id", "name", diff --git a/engine/e2e-test/api/model/test_api_model.py b/engine/e2e-test/api/model/test_api_model.py index bacf7e1b0..8677fffa0 100644 --- a/engine/e2e-test/api/model/test_api_model.py +++ b/engine/e2e-test/api/model/test_api_model.py @@ -95,6 +95,7 @@ async def test_models_start_stop_should_be_successful(self): time.sleep(30) print("Pull model") + requests.delete("http://localhost:3928/v1/models/tinyllama:1b") json_body = {"model": "tinyllama:1b"} response = requests.post("http://localhost:3928/v1/models/pull", json=json_body) assert response.status_code == 200, f"Failed to pull model: tinyllama:1b" diff --git a/engine/e2e-test/cli/engines/test_cli_engine_install.py b/engine/e2e-test/cli/engines/test_cli_engine_install.py index 370ebe3f3..673d66fa0 100644 --- a/engine/e2e-test/cli/engines/test_cli_engine_install.py +++ b/engine/e2e-test/cli/engines/test_cli_engine_install.py @@ -31,25 +31,9 @@ def test_engines_install_llamacpp_should_be_successfully(self): assert len(response.json()) > 0 assert exit_code == 0, f"Install engine failed with error: {error}" - @pytest.mark.skipif(reason="Ignore onnx-runtime test") - def test_engines_install_onnx_on_macos_should_be_failed(self): - exit_code, output, error = run( - "Install Engine", ["engines", "install", "onnxruntime"] - ) - assert "is not supported on" in output, "Should display error message" - assert exit_code == 0, f"Install engine failed with error: {error}" - - @pytest.mark.skipif(reason="Ignore tensorrt-llm test") - def test_engines_install_onnx_on_tensorrt_should_be_failed(self): - exit_code, output, error = run( - "Install Engine", ["engines", "install", "tensorrt-llm"] - ) - assert "is not supported on" in output, "Should display error message" - assert exit_code == 0, f"Install engine failed with error: {error}" - @pytest.mark.skipif(platform.system() == "Windows", reason="Progress bar log issue on Windows") def test_engines_install_pre_release_llamacpp(self): - engine_version = "v0.1.43" + engine_version = "b4920" exit_code, output, error = run( "Install Engine", ["engines", "install", "llama-cpp", "-v", engine_version], diff --git a/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py b/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py index 9fc296d60..ea3cae242 100644 --- a/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py +++ b/engine/e2e-test/runner/cortex-llamacpp-e2e-nightly.py @@ -21,7 +21,7 @@ from api.engines.test_api_get_default_engine import TestApiDefaultEngine from api.engines.test_api_get_engine_release import TestApiEngineRelease from api.engines.test_api_get_engine_release_latest import TestApiEngineReleaseLatest -from test_api_post_default_engine import TestApiSetDefaultEngine +from api.engines.test_api_post_default_engine import TestApiSetDefaultEngine from api.model.test_api_model import TestApiModel from api.model.test_api_model_import import TestApiModelImport from api.files.test_api_create_file import TestApiCreateFile diff --git a/engine/e2e-test/runner/main.py b/engine/e2e-test/runner/main.py index 49bdc5131..8a98d0ca3 100644 --- a/engine/e2e-test/runner/main.py +++ b/engine/e2e-test/runner/main.py @@ -21,7 +21,7 @@ from api.engines.test_api_get_default_engine import TestApiDefaultEngine from api.engines.test_api_get_engine_release import TestApiEngineRelease from api.engines.test_api_get_engine_release_latest import TestApiEngineReleaseLatest -from test_api_post_default_engine import TestApiSetDefaultEngine +from api.engines.test_api_post_default_engine import TestApiSetDefaultEngine from api.model.test_api_model import TestApiModel from api.model.test_api_model_import import TestApiModelImport from api.files.test_api_create_file import TestApiCreateFile diff --git a/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py b/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py index 7a3c2e232..a22000d93 100644 --- a/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py +++ b/engine/e2e-test/test_api_cortexso_hub_llamacpp_engine.py @@ -125,7 +125,7 @@ async def test_models_on_cortexso_hub(self, model_url): "Install Engine", ["engines", "install", "llama-cpp"], timeout=None, capture = False ) root = Path.home() - assert os.path.exists(root / "cortexcpp" / "engines" / "cortex.llamacpp" / "version.txt") + assert os.path.exists(root / "cortexcpp" / "engines" / "llama.cpp" / "version.txt") assert exit_code == 0, f"Install engine failed with error: {error}" # Start the model diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index f0bf64b4d..ac2f6274a 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -117,4 +117,11 @@ TEST_F(EngineMatcherUtilsTestSuite, TestGetVersionAndArch) { EXPECT_EQ(version, "b4920"); EXPECT_EQ(arch, "macos-x64"); } + + { + std::string variant = "llama-b4920-bin-ubuntu-vulkan-x64.zip"; + auto [version, arch] = engine_matcher_utils::GetVersionAndArch(variant); + EXPECT_EQ(version, "b4920"); + EXPECT_EQ(arch, "ubuntu-vulkan-x64"); + } } diff --git a/engine/test/components/test_github_release_utils.cc b/engine/test/components/test_github_release_utils.cc index 753e7270c..20c14b187 100644 --- a/engine/test/components/test_github_release_utils.cc +++ b/engine/test/components/test_github_release_utils.cc @@ -4,16 +4,16 @@ class GitHubReleaseUtilsTest : public ::testing::Test {}; TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseByVersion) { - auto version{"v0.1.36"}; + auto version{"b4920"}; auto result = github_release_utils::GetReleaseByVersion( - kMenloOrg, "cortex.llamacpp", version); + kMenloOrg, "llama.cpp", version); ASSERT_TRUE(result.has_value()); ASSERT_EQ(result->tag_name, version); } TEST_F(GitHubReleaseUtilsTest, AbleToGetReleaseList) { - auto result = github_release_utils::GetReleases(kMenloOrg, "cortex.llamacpp"); + auto result = github_release_utils::GetReleases(kMenloOrg, "llama.cpp"); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->size() > 0); From 7438f943be1f9124d5f5f8fb4fef176edd4c333a Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 19 Mar 2025 17:14:16 +0700 Subject: [PATCH 19/49] fix: validation --- engine/services/engine_service.cc | 2 +- .../components/test_engine_matcher_utils.cc | 32 +++++++++++++------ engine/utils/engine_matcher_utils.h | 4 +-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index a59bb3f53..9dc395c22 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -565,7 +565,7 @@ EngineService::GetEngineVariants(const std::string& engine, if (system_info->arch == "arm64" && name.find("arm64") != std::string::npos) arch_match = true; - if (system_info->arch == "amd64" && + if (system_info->arch == "x64" && name.find("x64") != std::string::npos) arch_match = true; diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index ac2f6274a..2c24a9b6f 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -22,17 +22,17 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { "llama-b4920-bin-ubuntu-vulkan-x64.tar.gz", "llama-b4920-bin-macos-arm64.zip", "llama-b4920-bin-macos-x64.zip", - "llama-b4920-bin-windows-amd64-avx-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-avx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-avx-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-avx-x64.zip", - "llama-b4920-bin-windows-amd64-avx2-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx2-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-avx2-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-avx2-x64.zip", - "llama-b4920-bin-windows-amd64-avx512-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-avx512-cuda-12-0.tar.gz", + "llama-b4920-bin-win-avx512-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-avx512-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-avx512-x64.zip", - "llama-b4920-bin-windows-amd64-noavx-cuda-11-7.tar.gz", - "llama-b4920-bin-windows-amd64-noavx-cuda-12-0.tar.gz", + "llama-b4920-bin-win-noavx-cuda-cu11.7-x64.tar.gz", + "llama-b4920-bin-win-noavx-cuda-cu12.0-x64.tar.gz", "llama-b4920-bin-win-noavx-x64.zip", "llama-b4920-bin-win-vulkan-x64.zip", }; @@ -41,14 +41,14 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { { auto os{"win"}; - auto cpu_arch{"amd64"}; + auto cpu_arch{"x64"}; auto suitable_avx{"avx2"}; auto cuda_version{"12.4"}; auto variant = engine_matcher_utils::Validate( cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); - EXPECT_EQ(variant, "llama-b4920-bin-windows-amd64-avx2-cuda-12-0.tar.gz"); + EXPECT_EQ(variant, "llama-b4920-bin-win-avx2-cuda-cu12.0-x64.tar.gz"); } { @@ -63,6 +63,18 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { EXPECT_EQ(variant, "llama-b4920-bin-macos-x64.zip"); } + { + auto os{"mac"}; + auto cpu_arch{"arm64"}; + auto suitable_avx{""}; + auto cuda_version{""}; + + auto variant = engine_matcher_utils::Validate( + cortex_llamacpp_variants, os, cpu_arch, suitable_avx, cuda_version); + + EXPECT_EQ(variant, "llama-b4920-bin-macos-arm64.zip"); + } + { auto os{"win"}; auto cpu_arch{"x64"}; diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index 0689fd726..1afdd194c 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -55,7 +55,7 @@ inline std::string GetSuitableAvxVariant(cortex::cpuid::CpuInfo& cpu_info) { inline std::string GetSuitableCudaVariant( const std::vector& variants, const std::string& cuda_version) { - std::regex cuda_reg("cuda-(\\d+)-(\\d+)"); + std::regex cuda_reg("cuda-cu(\\d+).(\\d+)"); std::smatch match; int requested_major = 0; @@ -148,7 +148,7 @@ inline std::string Validate(const std::vector& variants, const std::string& os, const std::string& cpu_arch, const std::string& suitable_avx, const std::string& cuda_version) { - CTL_INF(os << " " << cpu_arch); + // CTL_INF(os << " " << cpu_arch); // Early return if the OS is not supported if (os != kMacOs && os != kWindowsOs && os != kLinuxOs) { return ""; From bf16c812d26cf9237784995a0ec8b224067f2bf0 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Thu, 20 Mar 2025 05:48:15 +0700 Subject: [PATCH 20/49] fix: change GH user agent --- engine/utils/engine_constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/utils/engine_constants.h b/engine/utils/engine_constants.h index ee935f38a..695afb4c5 100644 --- a/engine/utils/engine_constants.h +++ b/engine/utils/engine_constants.h @@ -16,7 +16,7 @@ constexpr const auto kGgmlOrg = "ggml-org"; constexpr auto static kHuggingFaceHost = "huggingface.co"; constexpr auto static kGitHubHost = "api.github.com"; constexpr auto static kCortexFolderName = "cortexcpp"; -constexpr auto static kDefaultGHUserAgent = "cortexcpp"; +constexpr auto static kDefaultGHUserAgent = kMenloOrg; constexpr auto static kWindowsOs = "win"; constexpr auto static kMacOs = "mac"; From f7cb0ae07ec113c4e6a838a5d56f6f1f35cfaa39 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Thu, 20 Mar 2025 10:45:27 +0700 Subject: [PATCH 21/49] chore: windows patch --- .github/patches/windows/msvcp140.dll | Bin 590112 -> 576128 bytes .github/patches/windows/vcruntime140.dll | Bin 101664 -> 122984 bytes .github/patches/windows/vcruntime140_1.dll | Bin 44312 -> 49744 bytes engine/CMakeLists.txt | 9 +++++++++ 4 files changed, 9 insertions(+) diff --git a/.github/patches/windows/msvcp140.dll b/.github/patches/windows/msvcp140.dll index f999742d98acdff33fb4f60e6de7bab54531d8ac..d3d103ee0e851bda623d062aff376a73b0c67847 100644 GIT binary patch literal 576128 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%0D3Av(cp?e}Wk4rV#V?{j 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 RxY93hZYyluaY`KNe*ujg*G2#U literal 590112 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≪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

QE50%2ALHp6fO@9Xy+7 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�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

FJAZcLQt?v*_uHUVRZgPCE`& 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

From ee9e0ad5b55142959e6fd78ccbb273b993a2ade0 Mon Sep 17 00:00:00 2001 From: Service Account Date: Fri, 4 Jul 2025 14:37:57 +0700 Subject: [PATCH 49/49] chore: update readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ad4a379de..f56842d29 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -> ⚠️ **Archived Notice** -> This repository is no longer actively maintained. -> We have migrated development to [menloresearch/llama.cpp](https://github.com/menloresearch/llama.cpp). -> Please contribute directly to `llama.cpp` moving forward. +

# Cortex

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 z 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 z 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 diff --git a/.github/patches/windows/vcruntime140_1.dll b/.github/patches/windows/vcruntime140_1.dll index 3ebabdee66002f6a577fac2e55649ab2f2cc3fe6..2ef481dbf26bb96aecf6aaa66623fcc649a1fa96 100644 GIT binary patch literal 49744 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 literal 44312 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 DZTf$7 diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 1b40e97dc..d065706bc 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -225,3 +225,12 @@ set_target_properties(${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR} RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) + +if(MSVC) + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/../.github/patches/windows + ${CMAKE_BINARY_DIR}/ + ) +endif() \ No newline at end of file From 886222d1008cbf4308f696af683930448bd82177 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Thu, 20 Mar 2025 13:09:50 +0700 Subject: [PATCH 22/49] fix: windows --- .../extensions/local-engine/local_engine.cc | 4 ++- engine/utils/process/utils.cc | 28 +++++++++++-------- engine/utils/process/utils.h | 3 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index f6cbe5cdb..155fcc163 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -548,7 +548,9 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto log_path = (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); CTL_DBG("log: " << log_path); - auto result = cortex::process::SpawnProcess(v, log_path, log_path); + auto result = cortex::process::SpawnProcess( + v, log_path, log_path, + file_manager_utils::GetExecutableFolderContainerPath().string()); if (result.has_error()) { CTL_ERR("Fail to spawn process. " << result.error()); Json::Value error; diff --git a/engine/utils/process/utils.cc b/engine/utils/process/utils.cc index 8cd0adc64..f5135e955 100644 --- a/engine/utils/process/utils.cc +++ b/engine/utils/process/utils.cc @@ -42,7 +42,7 @@ std::vector ConvertToArgv(const std::vector& args) { cpp::result SpawnProcess( const std::vector& command, const std::string& stdout_file, - const std::string& stderr_file) { + const std::string& stderr_file, const std::string& current_directory) { std::stringstream ss; for (const auto item : command) { ss << item << " "; @@ -109,17 +109,21 @@ cpp::result SpawnProcess( // create a suspended process. we will resume it later after adding it to // a job (see below) - if (!CreateProcessA(NULL, // lpApplicationName - command_buffer, // lpCommandLine - NULL, // lpProcessAttributes - NULL, // lpThreadAttributes - TRUE, // bInheritHandles - CREATE_SUSPENDED, // dwCreationFlags - NULL, // lpEnvironment - NULL, // lpCurrentDirectory - &si, // lpStartupInfo - &pi // lpProcessInformation - )) { + if (!CreateProcessA( + NULL, // lpApplicationName + command_buffer, // lpCommandLine + NULL, // lpProcessAttributes + NULL, // lpThreadAttributes + TRUE, // bInheritHandles + CREATE_SUSPENDED, // dwCreationFlags + NULL, // lpEnvironment + current_directory.empty() + ? NULL + : const_cast( + current_directory.c_str()), // lpCurrentDirectory + &si, // lpStartupInfo + &pi // lpProcessInformation + )) { if (hStdOut != NULL) CloseHandle(hStdOut); if (hStdErr != NULL) diff --git a/engine/utils/process/utils.h b/engine/utils/process/utils.h index 19b821cef..126452707 100644 --- a/engine/utils/process/utils.h +++ b/engine/utils/process/utils.h @@ -36,7 +36,8 @@ std::vector ConvertToArgv(const std::vector& args); cpp::result SpawnProcess( const std::vector& command, - const std::string& stdout_file = "", const std::string& stderr_file = ""); + const std::string& stdout_file = "", const std::string& stderr_file = "", + const std::string& current_directory = ""); bool IsProcessAlive(ProcessInfo& proc_info); bool WaitProcess(ProcessInfo& proc_info); bool KillProcess(ProcessInfo& proc_info); From 87b2aa44a5ece9b7712435b71c9ea7ea239d1cbc Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Thu, 20 Mar 2025 14:01:43 +0700 Subject: [PATCH 23/49] chore: log --- engine/extensions/local-engine/local_engine.cc | 2 ++ engine/utils/dylib_path_manager.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 155fcc163..d4cb75fe6 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -548,6 +548,8 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto log_path = (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); CTL_DBG("log: " << log_path); + CTL_INF("exe path: " + << file_manager_utils::GetExecutableFolderContainerPath().string()); auto result = cortex::process::SpawnProcess( v, log_path, log_path, file_manager_utils::GetExecutableFolderContainerPath().string()); diff --git a/engine/utils/dylib_path_manager.cc b/engine/utils/dylib_path_manager.cc index 3d10fc8ff..052639424 100644 --- a/engine/utils/dylib_path_manager.cc +++ b/engine/utils/dylib_path_manager.cc @@ -26,7 +26,7 @@ cpp::result DylibPathManager::RegisterPath( } return cpp::fail("Failed to add DLL directory: " + path.string()); } else { - CTL_DBG("Added DLL directory: " << path.string()); + CTL_INF("Added DLL directory: " << path.string()); } dylib_paths.push_back({path, cookie}); From f866d5fb2b56a1960edfde096565ba118e1df4b4 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Fri, 21 Mar 2025 10:10:43 +0700 Subject: [PATCH 24/49] fix: handle macos 12 variants --- engine/cli/main.cc | 4 +- .../extensions/local-engine/local_engine.cc | 2 +- engine/extensions/local-engine/local_engine.h | 2 +- engine/services/engine_service.cc | 56 +++++++++++++++++-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/engine/cli/main.cc b/engine/cli/main.cc index fe6fdb39c..67bade220 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -148,7 +148,7 @@ int main(int argc, char* argv[]) { 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 @@ -171,7 +171,7 @@ int main(int argc, char* argv[]) { }; auto res = get_latest_version(); - + if (res.has_error()) { CTL_ERR("Failed to get latest llama.cpp version: " << res.error()); return; diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index f6cbe5cdb..a317dd622 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -1001,4 +1001,4 @@ void LocalEngine::HandleNonOpenAiChatCompletion( } } -} // namespace cortex::local \ No newline at end of file +} // namespace cortex::local diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h index 972a4482d..3501e21b6 100644 --- a/engine/extensions/local-engine/local_engine.h +++ b/engine/extensions/local-engine/local_engine.h @@ -71,4 +71,4 @@ class LocalEngine : public EngineI { TaskQueue& q_; }; -} // namespace cortex::local \ No newline at end of file +} // namespace cortex::local diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index bb4ad4af3..cc753054b 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -17,6 +17,7 @@ #include "utils/engine_matcher_utils.h" #include "utils/file_manager_utils.h" #include "utils/github_release_utils.h" +#include "utils/hardware/os_info.h" #include "utils/logging_utils.h" #include "utils/normalize_engine.h" #include "utils/result.hpp" @@ -528,16 +529,59 @@ EngineService::GetEngineVariants(const std::string& engine, std::vector compatible_variants; std::vector assets; + + auto get_os_major = []() -> int { + auto os_info = cortex::hw::GetOSInfo(); + // Get os major version + size_t dot_pos = os_info.version.find_first_of("."); + if (dot_pos != std::string::npos) { + try { + return std::stoi(os_info.version.substr(0, dot_pos)); + } catch (const std::exception& e) { + return 0; + } + } else { + // No version found + return 0; + } + }; + if (engine_release_menlo.has_value()) { - assets.insert(std::end(assets), - std::begin(engine_release_menlo.value().assets), - std::end(engine_release_menlo.value().assets)); + // In case of macos, if os version is 12, we get binary from menlo + std::copy_if( + engine_release_menlo.value().assets.begin(), + engine_release_menlo.value().assets.end(), std::back_inserter(assets), + [get_os_major](const github_release_utils::GitHubAsset& assets) { +#if defined(__APPLE__) && defined(__MACH__) + if (get_os_major() == 12 && + assets.name.find(kMacOs) != std::string::npos) { + return true; + } + return false; +#else + return true; +#endif + }); } + if (engine_release_ggml.has_value()) { - assets.insert(std::end(assets), - std::begin(engine_release_ggml.value().assets), - std::end(engine_release_ggml.value().assets)); + // In case of macos, if os version is 12, we get binary from menlo + std::copy_if( + engine_release_ggml.value().assets.begin(), + engine_release_ggml.value().assets.end(), std::back_inserter(assets), + [get_os_major](const github_release_utils::GitHubAsset& assets) { +#if defined(__APPLE__) && defined(__MACH__) + if (get_os_major() > 12 && + assets.name.find(kMacOs) != std::string::npos) { + return true; + } + return false; +#else + return true; +#endif + }); } + for (const auto& variant : assets) { CTL_INF("content_type: " << variant.content_type << ", name: " << variant.name); From de0de940679ddf97c0efc616f30076d114d61db3 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Fri, 21 Mar 2025 11:18:24 +0700 Subject: [PATCH 25/49] chore: e2e tests --- engine/e2e-test/api/engines/test_api_engine.py | 8 ++++---- .../e2e-test/api/engines/test_api_get_default_engine.py | 2 +- engine/e2e-test/api/engines/test_api_get_list_engine.py | 2 +- .../e2e-test/api/engines/test_api_post_default_engine.py | 2 +- engine/e2e-test/cli/engines/test_cli_engine_install.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/e2e-test/api/engines/test_api_engine.py b/engine/e2e-test/api/engines/test_api_engine.py index 34f277b60..842ef2c35 100644 --- a/engine/e2e-test/api/engines/test_api_engine.py +++ b/engine/e2e-test/api/engines/test_api_engine.py @@ -28,14 +28,14 @@ def test_engines_get_llamacpp_should_be_successful(self): # engines install def test_engines_install_llamacpp_specific_version_and_variant(self): - data = {"version": "b4920", "variant": "linux-avx-x64"} + data = {"version": "b4932", "variant": "linux-avx-x64"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) assert response.status_code == 200 def test_engines_install_llamacpp_specific_version_and_null_variant(self): - data = {"version": "b4920"} + data = {"version": "b4932"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) @@ -62,7 +62,7 @@ async def test_engines_install_uninstall_llamacpp_with_only_version_should_be_fa await wait_for_websocket_download_success_event(timeout=120) assert install_response.status_code == 200 - data = {"version": "b4920"} + data = {"version": "b4932"} response = requests.delete( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) @@ -85,7 +85,7 @@ async def test_engines_install_uninstall_llamacpp_with_variant_should_be_success def test_engines_install_uninstall_llamacpp_with_specific_variant_and_version_should_be_successful( self, ): - data = {"variant": "linux-avx-x64", "version": "b4920"} + data = {"variant": "linux-avx-x64", "version": "b4932"} # install first install_response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data diff --git a/engine/e2e-test/api/engines/test_api_get_default_engine.py b/engine/e2e-test/api/engines/test_api_get_default_engine.py index 4aef1623b..f0566128c 100644 --- a/engine/e2e-test/api/engines/test_api_get_default_engine.py +++ b/engine/e2e-test/api/engines/test_api_get_default_engine.py @@ -25,7 +25,7 @@ def test_api_get_default_engine_successfully(self): # Data test engine= "llama-cpp" name= "linux-avx-x64" - version= "b4920" + version= "b4932" data = {"version": version, "variant": name} post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" diff --git a/engine/e2e-test/api/engines/test_api_get_list_engine.py b/engine/e2e-test/api/engines/test_api_get_list_engine.py index b4249cb01..38cb45b39 100644 --- a/engine/e2e-test/api/engines/test_api_get_list_engine.py +++ b/engine/e2e-test/api/engines/test_api_get_list_engine.py @@ -25,7 +25,7 @@ def test_api_get_list_engines_successfully(self): # Data test engine= "llama-cpp" name= "linux-avx-x64" - version= "b4920" + version= "b4932" post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" response = requests.delete( diff --git a/engine/e2e-test/api/engines/test_api_post_default_engine.py b/engine/e2e-test/api/engines/test_api_post_default_engine.py index 24b29d503..cede78485 100644 --- a/engine/e2e-test/api/engines/test_api_post_default_engine.py +++ b/engine/e2e-test/api/engines/test_api_post_default_engine.py @@ -24,7 +24,7 @@ def test_api_set_default_engine_successfully(self): # Data test engine= "llama-cpp" name= "linux-avx-x64" - version= "b4920" + version= "b4932" data = {"version": version, "variant": name} post_install_url = f"http://localhost:3928/v1/engines/{engine}/install" diff --git a/engine/e2e-test/cli/engines/test_cli_engine_install.py b/engine/e2e-test/cli/engines/test_cli_engine_install.py index 673d66fa0..5d520ce8b 100644 --- a/engine/e2e-test/cli/engines/test_cli_engine_install.py +++ b/engine/e2e-test/cli/engines/test_cli_engine_install.py @@ -33,7 +33,7 @@ def test_engines_install_llamacpp_should_be_successfully(self): @pytest.mark.skipif(platform.system() == "Windows", reason="Progress bar log issue on Windows") def test_engines_install_pre_release_llamacpp(self): - engine_version = "b4920" + engine_version = "b4932" exit_code, output, error = run( "Install Engine", ["engines", "install", "llama-cpp", "-v", engine_version], From b783b220b37b189225ed73e07514ba12a62033b7 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Fri, 21 Mar 2025 11:22:56 +0700 Subject: [PATCH 26/49] chore: major version macos --- engine/services/engine_service.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index cc753054b..e17c839e4 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -553,7 +553,7 @@ EngineService::GetEngineVariants(const std::string& engine, engine_release_menlo.value().assets.end(), std::back_inserter(assets), [get_os_major](const github_release_utils::GitHubAsset& assets) { #if defined(__APPLE__) && defined(__MACH__) - if (get_os_major() == 12 && + if (get_os_major() <= 12 && assets.name.find(kMacOs) != std::string::npos) { return true; } @@ -563,7 +563,7 @@ EngineService::GetEngineVariants(const std::string& engine, #endif }); } - + if (engine_release_ggml.has_value()) { // In case of macos, if os version is 12, we get binary from menlo std::copy_if( From 7832a5a3dd3d0cefb7013c5ce9d9629dfd5fdbe4 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Fri, 21 Mar 2025 11:44:40 +0700 Subject: [PATCH 27/49] fix: windows e2e tests --- .../extensions/local-engine/local_engine.cc | 7 ++--- engine/services/engine_service.cc | 20 ++++++++++++- engine/utils/process/utils.cc | 28 ++++++++----------- engine/utils/process/utils.h | 3 +- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 275624662..247a90758 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -5,6 +5,7 @@ #include "utils/curl_utils.h" #include "utils/json_helper.h" #include "utils/logging_utils.h" +#include "utils/process/utils.h" #include "utils/url_parser.h" namespace cortex::local { @@ -548,11 +549,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, auto log_path = (file_manager_utils::GetCortexLogPath() / "logs" / "cortex.log").string(); CTL_DBG("log: " << log_path); - CTL_INF("exe path: " - << file_manager_utils::GetExecutableFolderContainerPath().string()); - auto result = cortex::process::SpawnProcess( - v, log_path, log_path, - file_manager_utils::GetExecutableFolderContainerPath().string()); + auto result = cortex::process::SpawnProcess(v, log_path, log_path); if (result.has_error()) { CTL_ERR("Fail to spawn process. " << result.error()); Json::Value error; diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index cc753054b..8d3c8a1ce 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -329,6 +329,24 @@ cpp::result EngineService::DownloadEngine( std::filesystem::perms::others_exec, std::filesystem::perm_options::add); + const std::vector windows_deps = { + "msvcp140.dll", "vcruntime140.dll", "vcruntime140_1.dll"}; + for (auto const& win_dep : windows_deps) { + if (std::filesystem::exists( + file_manager_utils::GetExecutableFolderContainerPath() / + win_dep)) { + CTL_INF("Copy file " + << (file_manager_utils::GetExecutableFolderContainerPath() / + win_dep) + .string() + << " to " << extract_path.string()); + std::filesystem::copy_file( + file_manager_utils::GetExecutableFolderContainerPath() / win_dep, + extract_path / win_dep, + std::filesystem::copy_options::overwrite_existing); + } + } + } catch (const std::exception& e) { CTL_INF(e.what()); } @@ -563,7 +581,7 @@ EngineService::GetEngineVariants(const std::string& engine, #endif }); } - + if (engine_release_ggml.has_value()) { // In case of macos, if os version is 12, we get binary from menlo std::copy_if( diff --git a/engine/utils/process/utils.cc b/engine/utils/process/utils.cc index 53558014b..f63de5c5e 100644 --- a/engine/utils/process/utils.cc +++ b/engine/utils/process/utils.cc @@ -42,7 +42,7 @@ std::vector ConvertToArgv(const std::vector& args) { cpp::result SpawnProcess( const std::vector& command, const std::string& stdout_file, - const std::string& stderr_file, const std::string& current_directory) { + const std::string& stderr_file) { std::stringstream ss; for (const auto& item : command) { ss << item << " "; @@ -109,21 +109,17 @@ cpp::result SpawnProcess( // create a suspended process. we will resume it later after adding it to // a job (see below) - if (!CreateProcessA( - NULL, // lpApplicationName - command_buffer, // lpCommandLine - NULL, // lpProcessAttributes - NULL, // lpThreadAttributes - TRUE, // bInheritHandles - CREATE_SUSPENDED, // dwCreationFlags - NULL, // lpEnvironment - current_directory.empty() - ? NULL - : const_cast( - current_directory.c_str()), // lpCurrentDirectory - &si, // lpStartupInfo - &pi // lpProcessInformation - )) { + if (!CreateProcessA(NULL, // lpApplicationName + command_buffer, // lpCommandLine + NULL, // lpProcessAttributes + NULL, // lpThreadAttributes + TRUE, // bInheritHandles + CREATE_SUSPENDED, // dwCreationFlags + NULL, // lpEnvironment + NULL, // lpCurrentDirectory + &si, // lpStartupInfo + &pi // lpProcessInformation + )) { if (hStdOut != NULL) CloseHandle(hStdOut); if (hStdErr != NULL) diff --git a/engine/utils/process/utils.h b/engine/utils/process/utils.h index 126452707..19b821cef 100644 --- a/engine/utils/process/utils.h +++ b/engine/utils/process/utils.h @@ -36,8 +36,7 @@ std::vector ConvertToArgv(const std::vector& args); cpp::result SpawnProcess( const std::vector& command, - const std::string& stdout_file = "", const std::string& stderr_file = "", - const std::string& current_directory = ""); + const std::string& stdout_file = "", const std::string& stderr_file = ""); bool IsProcessAlive(ProcessInfo& proc_info); bool WaitProcess(ProcessInfo& proc_info); bool KillProcess(ProcessInfo& proc_info); From 1c1146f1918b1e16a217caa7ffad30c6a927ee71 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Fri, 21 Mar 2025 11:57:56 +0700 Subject: [PATCH 28/49] fix: engine list --- engine/services/engine_service.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index e00af8874..8e3abe942 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -777,7 +777,7 @@ EngineService::GetInstalledEngineVariants(const std::string& engine) const { auto node = YAML::LoadFile(version_txt_path.string()); auto ev = EngineVariantResponse{ node["name"].as(), // name - "v" + node["version"].as(), // version + node["version"].as(), // version engine, // engine "", // type }; From 74873046104d859fa87d0b48d9085f00611fa233 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Fri, 21 Mar 2025 13:32:34 +0700 Subject: [PATCH 29/49] fix: macos filter --- engine/services/engine_service.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index 8e3abe942..89cd00058 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -571,8 +571,9 @@ EngineService::GetEngineVariants(const std::string& engine, engine_release_menlo.value().assets.end(), std::back_inserter(assets), [get_os_major](const github_release_utils::GitHubAsset& assets) { #if defined(__APPLE__) && defined(__MACH__) - if (get_os_major() <= 12 && - assets.name.find(kMacOs) != std::string::npos) { + if ((assets.name.find(kMacOs) == std::string::npos) || + (get_os_major() <= 12 && + assets.name.find(kMacOs) != std::string::npos)) { return true; } return false; @@ -589,8 +590,9 @@ EngineService::GetEngineVariants(const std::string& engine, engine_release_ggml.value().assets.end(), std::back_inserter(assets), [get_os_major](const github_release_utils::GitHubAsset& assets) { #if defined(__APPLE__) && defined(__MACH__) - if (get_os_major() > 12 && - assets.name.find(kMacOs) != std::string::npos) { + if ((assets.name.find(kMacOs) == std::string::npos) || + (get_os_major() > 12 && + assets.name.find(kMacOs) != std::string::npos)) { return true; } return false; @@ -776,10 +778,10 @@ EngineService::GetInstalledEngineVariants(const std::string& engine) const { try { auto node = YAML::LoadFile(version_txt_path.string()); auto ev = EngineVariantResponse{ - node["name"].as(), // name + node["name"].as(), // name node["version"].as(), // version - engine, // engine - "", // type + engine, // engine + "", // type }; variants.push_back(ev); } catch (const YAML::Exception& e) { From 1e9986139cfd5505fdaa471d46f103febfb3e994 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Fri, 21 Mar 2025 15:10:01 +0700 Subject: [PATCH 30/49] chore: skips some tests for linux arm --- engine/e2e-test/api/model/test_api_model.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/engine/e2e-test/api/model/test_api_model.py b/engine/e2e-test/api/model/test_api_model.py index 8677fffa0..f370b1daa 100644 --- a/engine/e2e-test/api/model/test_api_model.py +++ b/engine/e2e-test/api/model/test_api_model.py @@ -1,6 +1,7 @@ import pytest import requests import time +import platform from utils.test_runner import ( run, start_server, @@ -111,16 +112,18 @@ async def test_models_start_stop_should_be_successful(self): response = requests.get("http://localhost:3928/v1/models") assert response.status_code == 200 - print("Start model") - json_body = {"model": "tinyllama:1b"} - response = requests.post( - "http://localhost:3928/v1/models/start", json=json_body - ) - assert response.status_code == 200, f"status_code: {response.status_code}" + # Skip tests for linux arm + if platform.machine() != "aarch64": + print("Start model") + json_body = {"model": "tinyllama:1b"} + response = requests.post( + "http://localhost:3928/v1/models/start", json=json_body + ) + assert response.status_code == 200, f"status_code: {response.status_code}" - print("Stop model") - response = requests.post("http://localhost:3928/v1/models/stop", json=json_body) - assert response.status_code == 200, f"status_code: {response.status_code}" + print("Stop model") + response = requests.post("http://localhost:3928/v1/models/stop", json=json_body) + assert response.status_code == 200, f"status_code: {response.status_code}" # update API print("Update model") From 70adc6efecfe005b85fc6d282f7ba9cff6003c51 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Mon, 24 Mar 2025 09:48:04 +0700 Subject: [PATCH 31/49] fix: terminate process windows --- engine/utils/process/utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/utils/process/utils.cc b/engine/utils/process/utils.cc index f63de5c5e..c9ccddfdf 100644 --- a/engine/utils/process/utils.cc +++ b/engine/utils/process/utils.cc @@ -347,7 +347,7 @@ bool KillProcess(ProcessInfo& proc_info) { bool success; #if defined(_WIN32) - success = TerminateJobObject(proc_info.hJob, 0) == 0; + success = TerminateJobObject(proc_info.hJob, 0); #elif defined(__APPLE__) || defined(__linux__) // we send SIGTERM to subprocess. we trust that this subprocess will // propagate SIGTERM correctly to its children processes. From 9846ad80f58b9bfba5bdd3ba0cb6bd1d0686820a Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Mon, 24 Mar 2025 11:53:52 +0700 Subject: [PATCH 32/49] chore: release CIs (#2171) * chore: release CIs * fix: cuda urls --------- Co-authored-by: sangjanai --- .github/workflows/beta-build.yml | 20 +++++------ .github/workflows/nightly-build.yml | 26 +++++++------- .github/workflows/stable-build.yml | 20 +++++------ .github/workflows/template-build-linux.yml | 34 +++++++++---------- .github/workflows/template-build-macos.yml | 6 ++-- .../workflows/template-build-windows-x64.yml | 32 ++++++++--------- ...emplate-cortex-llamacpp-latest-version.yml | 10 +++--- 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index 1bf324d96..1d5480312 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -9,7 +9,7 @@ jobs: get-update-version: uses: ./.github/workflows/template-get-update-version.yml - get-cortex-llamacpp-latest-version: + get-llamacpp-latest-version: uses: ./.github/workflows/template-cortex-llamacpp-latest-version.yml create-draft-release: @@ -39,7 +39,7 @@ jobs: build-macos: uses: ./.github/workflows/template-build-macos.yml - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] secrets: inherit with: ref: ${{ github.ref }} @@ -48,12 +48,12 @@ jobs: cmake-flags: "-DCORTEX_VARIANT=beta -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" channel: beta upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} build-windows-x64: uses: ./.github/workflows/template-build-windows-x64.yml secrets: inherit - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] with: ref: ${{ github.ref }} public_provider: github @@ -64,12 +64,12 @@ jobs: ccache-dir: 'C:\Users\ContainerAdministrator\AppData\Local\ccache' channel: beta upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} build-linux-x64: uses: ./.github/workflows/template-build-linux.yml secrets: inherit - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] with: ref: ${{ github.ref }} public_provider: github @@ -78,13 +78,13 @@ jobs: cmake-flags: "-DCORTEX_VARIANT=beta -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" channel: beta upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: amd64 build-linux-arm64: uses: ./.github/workflows/template-build-linux.yml secrets: inherit - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] with: ref: ${{ github.ref }} public_provider: github @@ -93,13 +93,13 @@ jobs: cmake-flags: "-DCORTEX_VARIANT=beta -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" channel: beta upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: arm64 build-docker-x64: uses: ./.github/workflows/template-build-docker-x64.yml secrets: inherit - needs: [get-update-version, get-cortex-llamacpp-latest-version] + needs: [get-update-version, get-llamacpp-latest-version] with: ref: ${{ github.ref }} new_version: ${{ needs.get-update-version.outputs.new_version }} diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 1f076dc97..efdbfdf6f 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -43,12 +43,12 @@ jobs: get-update-version: uses: ./.github/workflows/template-get-update-version.yml - get-cortex-llamacpp-latest-version: + get-llamacpp-latest-version: uses: ./.github/workflows/template-cortex-llamacpp-latest-version.yml build-macos: uses: ./.github/workflows/template-build-macos.yml - needs: [get-update-version, set-public-provider, get-cortex-llamacpp-latest-version] + needs: [get-update-version, set-public-provider, get-llamacpp-latest-version] secrets: inherit with: ref: ${{ needs.set-public-provider.outputs.ref }} @@ -56,12 +56,12 @@ jobs: new_version: ${{ needs.get-update-version.outputs.new_version }} cmake-flags: "-DCORTEX_VARIANT=nightly -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" channel: nightly - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} build-windows-x64: uses: ./.github/workflows/template-build-windows-x64.yml secrets: inherit - needs: [get-update-version, set-public-provider, get-cortex-llamacpp-latest-version] + needs: [get-update-version, set-public-provider, get-llamacpp-latest-version] with: ref: ${{ needs.set-public-provider.outputs.ref }} public_provider: ${{ needs.set-public-provider.outputs.public_provider }} @@ -71,12 +71,12 @@ jobs: build-deps-cmake-flags: "-DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache -GNinja" ccache-dir: 'C:\Users\ContainerAdministrator\AppData\Local\ccache' channel: nightly - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} build-linux-x64: uses: ./.github/workflows/template-build-linux.yml secrets: inherit - needs: [get-update-version, set-public-provider, get-cortex-llamacpp-latest-version] + needs: [get-update-version, set-public-provider, get-llamacpp-latest-version] with: ref: ${{ needs.set-public-provider.outputs.ref }} public_provider: ${{ needs.set-public-provider.outputs.public_provider }} @@ -84,13 +84,13 @@ jobs: runs-on: ubuntu-20-04 cmake-flags: "-DCORTEX_VARIANT=nightly -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" channel: nightly - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: amd64 build-linux-arm64: uses: ./.github/workflows/template-build-linux.yml secrets: inherit - needs: [get-update-version, set-public-provider, get-cortex-llamacpp-latest-version] + needs: [get-update-version, set-public-provider, get-llamacpp-latest-version] with: ref: ${{ needs.set-public-provider.outputs.ref }} public_provider: ${{ needs.set-public-provider.outputs.public_provider }} @@ -98,13 +98,13 @@ jobs: runs-on: ubuntu-2004-arm64 cmake-flags: "-DCORTEX_VARIANT=nightly -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" channel: nightly - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: arm64 update-latest-version: runs-on: ubuntu-latest if: needs.set-public-provider.outputs.public_provider == 'aws-s3' - needs: [get-update-version, set-public-provider, build-linux-x64, build-linux-arm64, build-macos, build-windows-x64, get-cortex-llamacpp-latest-version] + needs: [get-update-version, set-public-provider, build-linux-x64, build-linux-arm64, build-macos, build-windows-x64, get-llamacpp-latest-version] steps: - name: Update latest version id: update-latest-version @@ -132,7 +132,7 @@ jobs: if: needs.set-public-provider.outputs.public_provider == 'aws-s3' uses: ./.github/workflows/template-build-docker-x64.yml secrets: inherit - needs: [get-update-version, set-public-provider, get-cortex-llamacpp-latest-version, update-latest-version] + needs: [get-update-version, set-public-provider, get-llamacpp-latest-version, update-latest-version] with: ref: ${{ needs.set-public-provider.outputs.ref }} new_version: nightly-${{ needs.get-update-version.outputs.new_version }} @@ -141,7 +141,7 @@ jobs: tags: menloltd/cortex:nightly-${{ needs.get-update-version.outputs.new_version }} noti-discord-nightly-and-update-url-readme: - needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, get-cortex-llamacpp-latest-version, update-latest-version, build-docker-x64] + needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, get-llamacpp-latest-version, update-latest-version, build-docker-x64] secrets: inherit if: github.event_name == 'schedule' uses: ./.github/workflows/template-noti-discord.yaml @@ -150,7 +150,7 @@ jobs: new_version: ${{ needs.get-update-version.outputs.new_version }} noti-discord-manual: - needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, get-cortex-llamacpp-latest-version, build-docker-x64] + needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, get-llamacpp-latest-version, build-docker-x64] secrets: inherit if: github.event_name == 'workflow_dispatch' && github.event.inputs.public_provider == 'aws-s3' uses: ./.github/workflows/template-noti-discord.yaml diff --git a/.github/workflows/stable-build.yml b/.github/workflows/stable-build.yml index b05df983d..c4b5f53f3 100644 --- a/.github/workflows/stable-build.yml +++ b/.github/workflows/stable-build.yml @@ -9,7 +9,7 @@ jobs: get-update-version: uses: ./.github/workflows/template-get-update-version.yml - get-cortex-llamacpp-latest-version: + get-llamacpp-latest-version: uses: ./.github/workflows/template-cortex-llamacpp-latest-version.yml create-draft-release: @@ -39,7 +39,7 @@ jobs: build-macos: uses: ./.github/workflows/template-build-macos.yml - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] secrets: inherit with: ref: ${{ github.ref }} @@ -48,12 +48,12 @@ jobs: cmake-flags: "-DCORTEX_VARIANT=prod -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" channel: stable upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} build-windows-x64: uses: ./.github/workflows/template-build-windows-x64.yml secrets: inherit - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] with: ref: ${{ github.ref }} public_provider: github @@ -64,12 +64,12 @@ jobs: ccache-dir: 'C:\Users\ContainerAdministrator\AppData\Local\ccache' channel: stable upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} build-linux-x64: uses: ./.github/workflows/template-build-linux.yml secrets: inherit - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] with: ref: ${{ github.ref }} public_provider: github @@ -78,13 +78,13 @@ jobs: cmake-flags: "-DCORTEX_VARIANT=prod -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" channel: stable upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: amd64 build-linux-arm64: uses: ./.github/workflows/template-build-linux.yml secrets: inherit - needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] + needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] with: ref: ${{ github.ref }} public_provider: github @@ -93,13 +93,13 @@ jobs: cmake-flags: "-DCORTEX_VARIANT=prod -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" channel: stable upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: arm64 build-docker-x64: uses: ./.github/workflows/template-build-docker-x64.yml secrets: inherit - needs: [get-update-version, get-cortex-llamacpp-latest-version] + needs: [get-update-version, get-llamacpp-latest-version] with: ref: ${{ github.ref }} new_version: ${{ needs.get-update-version.outputs.new_version }} diff --git a/.github/workflows/template-build-linux.yml b/.github/workflows/template-build-linux.yml index 3fa802ad4..a50b8298e 100644 --- a/.github/workflows/template-build-linux.yml +++ b/.github/workflows/template-build-linux.yml @@ -44,7 +44,7 @@ on: type: string default: 'nightly' description: 'The channel to use for this job' - cortex-llamacpp-version: + llamacpp-version: required: true type: string default: '0.0.0' @@ -169,23 +169,23 @@ jobs: mkdir -p engine/templates/linux/dependencies cd engine/templates/linux/dependencies if [ "${{ inputs.arch }}" == "amd64" ]; then - # wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx-cuda-11-7.tar.gz - # wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx-cuda-12-0.tar.gz - # wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx2-cuda-11-7.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx2-cuda-12-0.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx2.tar.gz - # wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx512-cuda-11-7.tar.gz - # wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx512-cuda-12-0.tar.gz - # wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx512.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-noavx-cuda-11-7.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-noavx-cuda-12-0.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-noavx.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-vulkan.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-11-7-linux-amd64.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-12-0-linux-amd64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-cuda-cu11.7-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-cuda-cu12.0-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx2-cuda-cu11.7-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx2-cuda-cu12.0-x64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-x64.zip + # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-cuda-cu11.7-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-cuda-cu12.0-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-cuda-cu11.7-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-cuda-cu12.0-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-x64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-vulkan-x64.zip + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-linux-cu11.7-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu12.0-x64.tar.gz else - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-arm64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-arm64.zip fi cd .. diff --git a/.github/workflows/template-build-macos.yml b/.github/workflows/template-build-macos.yml index 20c7430fb..5eb0122d0 100644 --- a/.github/workflows/template-build-macos.yml +++ b/.github/workflows/template-build-macos.yml @@ -39,7 +39,7 @@ on: type: string default: 'nightly' description: 'The channel to use for this job' - cortex-llamacpp-version: + llamacpp-version: required: true type: string default: '0.0.0' @@ -289,8 +289,8 @@ jobs: run: | mkdir -p engine/templates/macos/Scripts/dependencies cd engine/templates/macos/Scripts/dependencies - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-mac-arm64.tar.gz - wget https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-mac-amd64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-macos-arm64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-macos-x64.tar.gz cd ../../ chmod +x create_pkg_local.sh diff --git a/.github/workflows/template-build-windows-x64.yml b/.github/workflows/template-build-windows-x64.yml index b9e0c9937..896826464 100644 --- a/.github/workflows/template-build-windows-x64.yml +++ b/.github/workflows/template-build-windows-x64.yml @@ -44,7 +44,7 @@ on: type: string default: 'nightly' description: 'The channel to use for this job' - cortex-llamacpp-version: + llamacpp-version: required: true type: string default: '0.0.0' @@ -205,21 +205,21 @@ jobs: run: | mkdir dependencies cd dependencies - # wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx-cuda-11-7.tar.gz - # wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx-cuda-12-0.tar.gz - # wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx2-cuda-11-7.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx2-cuda-12-0.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx2.tar.gz - # wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx512-cuda-11-7.tar.gz - # wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx512-cuda-12-0.tar.gz - # wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-avx512.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-noavx-cuda-11-7.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-noavx-cuda-12-0.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-noavx.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-windows-amd64-vulkan.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-11-7-windows-amd64.tar.gz - wget.exe https://github.com/menloresearch/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-12-0-windows-amd64.tar.gz + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-cuda-cu11.7-x64.tar.gz + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-cuda-cu12.0-x64.tar.gz + # wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-x64.zip + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-cuda-cu11.7-x64.tar.gz + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-cuda-cu12.0-x64.tar.gz + wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-x64.zip + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-cuda-cu11.7-x64.tar.gz + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-cuda-cu12.0-x64.tar.gz + # wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-x64.zip + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-cuda-cu11.7-x64.tar.gz + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-cuda-cu12.0-x64.tar.gz + wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-x64.zip + wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-vulkan-x64.zip + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu11.7-x64.tar.gz + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu12.0-x64.tar.gz - name: Enable long paths run: | diff --git a/.github/workflows/template-cortex-llamacpp-latest-version.yml b/.github/workflows/template-cortex-llamacpp-latest-version.yml index 610b1a89a..3d7b74e56 100644 --- a/.github/workflows/template-cortex-llamacpp-latest-version.yml +++ b/.github/workflows/template-cortex-llamacpp-latest-version.yml @@ -1,13 +1,13 @@ -name: get-cortex-llamacpp-latest-version +name: get-llamacpp-latest-version on: workflow_call: outputs: - cortex_llamacpp_latest_version: + llamacpp_latest_version: description: 'The latest version of cortex.llamacpp engines' - value: ${{ jobs.get-cortex-llamacpp-latest-version.outputs.new_version }} + value: ${{ jobs.get-llamacpp-latest-version.outputs.new_version }} jobs: - get-cortex-llamacpp-latest-version: + get-llamacpp-latest-version: runs-on: ubuntu-latest outputs: new_version: ${{ steps.version_update.outputs.new_version }} @@ -24,7 +24,7 @@ jobs: local max_retries=3 local tag while [ $retries -lt $max_retries ]; do - tag=$(curl -s https://api.github.com/repos/menloresearch/cortex.llamacpp/releases/latest | jq -r .tag_name) + tag=$(curl -s https://api.github.com/repos/menloresearch/llama.cpp/releases/latest | jq -r .tag_name) if [ -n "$tag" ] && [ "$tag" != "null" ]; then echo $tag return From aba833e138f490e242d513f0801708145d33f62f Mon Sep 17 00:00:00 2001 From: sangjanai Date: Mon, 24 Mar 2025 13:02:45 +0700 Subject: [PATCH 33/49] fix: remove v in the llama-engine wget --- .github/workflows/template-build-linux.yml | 32 +++++++++---------- .github/workflows/template-build-macos.yml | 4 +-- .../workflows/template-build-windows-x64.yml | 30 ++++++++--------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/template-build-linux.yml b/.github/workflows/template-build-linux.yml index a50b8298e..0ebd04176 100644 --- a/.github/workflows/template-build-linux.yml +++ b/.github/workflows/template-build-linux.yml @@ -169,23 +169,23 @@ jobs: mkdir -p engine/templates/linux/dependencies cd engine/templates/linux/dependencies if [ "${{ inputs.arch }}" == "amd64" ]; then - # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-cuda-cu11.7-x64.tar.gz - # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-cuda-cu12.0-x64.tar.gz - # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-x64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx2-cuda-cu11.7-x64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx2-cuda-cu12.0-x64.tar.gz - wget https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-x64.zip - # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-cuda-cu11.7-x64.tar.gz - # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-cuda-cu12.0-x64.tar.gz - # wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-x64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-cuda-cu11.7-x64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-cuda-cu12.0-x64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-x64.tar.gz - wget https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-vulkan-x64.zip - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-linux-cu11.7-x64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu12.0-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-cuda-cu11.7-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-cuda-cu12.0-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx2-cuda-cu11.7-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx2-cuda-cu12.0-x64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-x64.zip + # wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-cuda-cu11.7-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-cuda-cu12.0-x64.tar.gz + # wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-avx512-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-cuda-cu11.7-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-cuda-cu12.0-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-linux-noavx-x64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-vulkan-x64.zip + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/cudart-llama-bin-linux-cu11.7-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu12.0-x64.tar.gz else - wget https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-arm64.zip + wget https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-ubuntu-arm64.zip fi cd .. diff --git a/.github/workflows/template-build-macos.yml b/.github/workflows/template-build-macos.yml index 5eb0122d0..038546097 100644 --- a/.github/workflows/template-build-macos.yml +++ b/.github/workflows/template-build-macos.yml @@ -289,8 +289,8 @@ jobs: run: | mkdir -p engine/templates/macos/Scripts/dependencies cd engine/templates/macos/Scripts/dependencies - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-macos-arm64.tar.gz - wget https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-macos-x64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-macos-arm64.tar.gz + wget https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-macos-x64.tar.gz cd ../../ chmod +x create_pkg_local.sh diff --git a/.github/workflows/template-build-windows-x64.yml b/.github/workflows/template-build-windows-x64.yml index 896826464..399e3dd3e 100644 --- a/.github/workflows/template-build-windows-x64.yml +++ b/.github/workflows/template-build-windows-x64.yml @@ -205,21 +205,21 @@ jobs: run: | mkdir dependencies cd dependencies - # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-cuda-cu11.7-x64.tar.gz - # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-cuda-cu12.0-x64.tar.gz - # wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-x64.zip - wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-cuda-cu11.7-x64.tar.gz - wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-cuda-cu12.0-x64.tar.gz - wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-x64.zip - # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-cuda-cu11.7-x64.tar.gz - # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-cuda-cu12.0-x64.tar.gz - # wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-x64.zip - wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-cuda-cu11.7-x64.tar.gz - wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-cuda-cu12.0-x64.tar.gz - wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-x64.zip - wget.exe https://github.com/ggml-org/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-vulkan-x64.zip - wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu11.7-x64.tar.gz - wget.exe https://github.com/menloresearch/llama.cpp/releases/download/v${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu12.0-x64.tar.gz + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-cuda-cu11.7-x64.tar.gz + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-cuda-cu12.0-x64.tar.gz + # wget.exe https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx-x64.zip + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-cuda-cu11.7-x64.tar.gz + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-cuda-cu12.0-x64.tar.gz + wget.exe https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx2-x64.zip + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-cuda-cu11.7-x64.tar.gz + # wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-cuda-cu12.0-x64.tar.gz + # wget.exe https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-avx512-x64.zip + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-cuda-cu11.7-x64.tar.gz + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-cuda-cu12.0-x64.tar.gz + wget.exe https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-noavx-x64.zip + wget.exe https://github.com/ggml-org/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/llama-${{ inputs.llamacpp-version }}-bin-win-vulkan-x64.zip + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu11.7-x64.tar.gz + wget.exe https://github.com/menloresearch/llama.cpp/releases/download/${{ inputs.llamacpp-version }}/cudart-llama-bin-win-cu12.0-x64.tar.gz - name: Enable long paths run: | From b239378c853b91943efaec8127675f8a4831334c Mon Sep 17 00:00:00 2001 From: sangjanai Date: Mon, 24 Mar 2025 13:47:27 +0700 Subject: [PATCH 34/49] fix: add start time for model --- .../extensions/local-engine/local_engine.cc | 32 ++++++++++++++++++- engine/extensions/local-engine/local_engine.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 247a90758..885c14d77 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -566,6 +566,8 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, s.process_info = result.value(); if (wait_for_server_up(model_id, s.host, s.port)) { + s.start_time = std::chrono::system_clock::now().time_since_epoch() / + std::chrono::milliseconds(1); Json::Value response; response["status"] = "Model loaded successfully with pid: " + std::to_string(s.process_info.pid); @@ -665,7 +667,35 @@ void LocalEngine::GetModelStatus(std::shared_ptr json_body, } void LocalEngine::GetModels(std::shared_ptr json_body, - http_callback&& callback) {} + http_callback&& callback) { + Json::Value json_resp; + Json::Value model_array(Json::arrayValue); + { + for (const auto& [m, s] : server_map_) { + Json::Value val; + val["id"] = m; + val["engine"] = kLlamaEngine; + val["start_time"] = s.start_time; + val["model_size"] = 0u; + val["vram"] = 0u; + val["ram"] = 0u; + val["object"] = "model"; + model_array.append(val); + } + } + + json_resp["object"] = "list"; + json_resp["data"] = model_array; + + Json::Value status; + status["is_done"] = true; + status["has_error"] = false; + status["is_stream"] = false; + status["status_code"] = 200; + callback(std::move(status), std::move(json_resp)); + CTL_INF("Running models responded"); + (void)json_body; +} void LocalEngine::HandleOpenAiChatCompletion( std::shared_ptr json_body, http_callback&& callback, diff --git a/engine/extensions/local-engine/local_engine.h b/engine/extensions/local-engine/local_engine.h index 3501e21b6..6dd970799 100644 --- a/engine/extensions/local-engine/local_engine.h +++ b/engine/extensions/local-engine/local_engine.h @@ -21,6 +21,7 @@ struct ServerAddress { std::string user_prompt; std::string ai_prompt; std::string system_prompt; + uint64_t start_time; }; class LocalEngine : public EngineI { From 552db8fa6ee2d43488176db5dfeb51ac497bd2f9 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Thu, 27 Mar 2025 14:45:44 +0700 Subject: [PATCH 35/49] fix: std::filesystem::equivalent does not work for non-exist path (#2182) * fix: std::filesystem::equivalent does not work for non-exist path * chore: enable create file test for all platforms (#2183) --------- Co-authored-by: sangjanai --- engine/e2e-test/api/files/test_api_create_file.py | 1 - engine/repositories/file_fs_repository.cc | 10 +++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/engine/e2e-test/api/files/test_api_create_file.py b/engine/e2e-test/api/files/test_api_create_file.py index 7c7226f50..03525672d 100644 --- a/engine/e2e-test/api/files/test_api_create_file.py +++ b/engine/e2e-test/api/files/test_api_create_file.py @@ -23,7 +23,6 @@ def setup_and_teardown(self): # Teardown stop_server() - @pytest.mark.skipif(platform.system() != "Linux", reason="Todo: fix later on Mac and Window") def test_api_create_file_successfully(self): # Define file path file_path_rel = os.path.join("e2e-test", "api", "files", "blank.txt") diff --git a/engine/repositories/file_fs_repository.cc b/engine/repositories/file_fs_repository.cc index f5b349f45..67c0981ba 100644 --- a/engine/repositories/file_fs_repository.cc +++ b/engine/repositories/file_fs_repository.cc @@ -18,14 +18,10 @@ std::filesystem::path SanitizePath(const std::filesystem::path& user_input, std::filesystem::path resolved_path = std::filesystem::weakly_canonical( std::filesystem::path(basedir) / std::filesystem::path(user_input)); /* Ensure the resolved path is within our basedir */ - for (auto p = resolved_path; !p.empty(); p = p.parent_path()) { - if (std::filesystem::equivalent(p, abs_base)) { - return resolved_path; - } - if (p == p.parent_path()) { // reached the root directory - break; - } + if (resolved_path.string().find(abs_base.string()) != std::string::npos) { + return resolved_path; } + return {}; } From 5f3650142a27ff75de6811f8ee6555d5b05d3ee8 Mon Sep 17 00:00:00 2001 From: sangjanai Date: Wed, 2 Apr 2025 16:54:18 +0700 Subject: [PATCH 36/49] chore: quality gate --- .github/workflows/cortex-cpp-quality-gate.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cortex-cpp-quality-gate.yml b/.github/workflows/cortex-cpp-quality-gate.yml index 279dd77d6..fc2d52b63 100644 --- a/.github/workflows/cortex-cpp-quality-gate.yml +++ b/.github/workflows/cortex-cpp-quality-gate.yml @@ -150,6 +150,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "huggingFaceToken: ${{ secrets.HUGGINGFACE_TOKEN_READ }}" > ~/.config/cortexcpp/.cortexrc echo "gitHubToken: ${{ secrets.PAT_SERVICE_ACCOUNT }}" >> ~/.config/cortexcpp/.cortexrc # ./build/cortex @@ -177,6 +178,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "apiServerPort: 3928" > ~/.config/cortexcpp/.cortexrc echo "huggingFaceToken: ${{ secrets.HUGGINGFACE_TOKEN_READ }}" >> ~/.config/cortexcpp/.cortexrc echo "gitHubToken: ${{ secrets.PAT_SERVICE_ACCOUNT }}" >> ~/.config/cortexcpp/.cortexrc @@ -456,6 +458,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "gitHubToken: ${{ secrets.GITHUB_TOKEN }}" > ~/.config/cortexcpp/.cortexrc # ./build/cortex cat ~/.config/cortexcpp/.cortexrc @@ -481,6 +484,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "apiServerPort: 3928" > ~/.config/cortexcpp/.cortexrc echo "gitHubToken: ${{ secrets.GITHUB_TOKEN }}" > ~/.config/cortexcpp/.cortexrc # ./build/cortex From f7f21e475c6579f10b3de19eef4e0673eccedecd Mon Sep 17 00:00:00 2001 From: Nguyen Ngoc Minh <91668012+Minh141120@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:24:54 +0700 Subject: [PATCH 37/49] chore: download cmake version 3.22.6 (#2194) * chore: download cmake version 3.22.6 * chore: remove cmake install deps step * chore: trigger quality gate ci to test * chore: update run setup config --------- Co-authored-by: sangjanai --- .github/workflows/cortex-cpp-quality-gate.yml | 4 ++++ docker/Dockerfile | 13 +++++++++++-- docker/Dockerfile.cache | 13 +++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cortex-cpp-quality-gate.yml b/.github/workflows/cortex-cpp-quality-gate.yml index 279dd77d6..fc2d52b63 100644 --- a/.github/workflows/cortex-cpp-quality-gate.yml +++ b/.github/workflows/cortex-cpp-quality-gate.yml @@ -150,6 +150,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "huggingFaceToken: ${{ secrets.HUGGINGFACE_TOKEN_READ }}" > ~/.config/cortexcpp/.cortexrc echo "gitHubToken: ${{ secrets.PAT_SERVICE_ACCOUNT }}" >> ~/.config/cortexcpp/.cortexrc # ./build/cortex @@ -177,6 +178,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "apiServerPort: 3928" > ~/.config/cortexcpp/.cortexrc echo "huggingFaceToken: ${{ secrets.HUGGINGFACE_TOKEN_READ }}" >> ~/.config/cortexcpp/.cortexrc echo "gitHubToken: ${{ secrets.PAT_SERVICE_ACCOUNT }}" >> ~/.config/cortexcpp/.cortexrc @@ -456,6 +458,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "gitHubToken: ${{ secrets.GITHUB_TOKEN }}" > ~/.config/cortexcpp/.cortexrc # ./build/cortex cat ~/.config/cortexcpp/.cortexrc @@ -481,6 +484,7 @@ jobs: run: | cd engine mkdir -p ~/.config/cortexcpp/ + mkdir -p ~/.local/share/cortexcpp/ echo "apiServerPort: 3928" > ~/.config/cortexcpp/.cortexrc echo "gitHubToken: ${{ secrets.GITHUB_TOKEN }}" > ~/.config/cortexcpp/.cortexrc # ./build/cortex diff --git a/docker/Dockerfile b/docker/Dockerfile index 744c3899c..5f04da12e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -24,7 +24,6 @@ RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/nul apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" && \ apt-get update && \ apt-get install -y --no-install-recommends \ - cmake \ make \ git \ uuid-dev \ @@ -37,11 +36,21 @@ RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/nul ninja-build \ pkg-config \ python3-pip \ - openssl && \ + openssl \ + libssl-dev && \ pip3 install awscli && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* +# Download and install CMake 3.22.6 +RUN wget https://github.com/Kitware/CMake/releases/download/v3.22.6/cmake-3.22.6.tar.gz -q -O /tmp/cmake.tar.gz && \ + tar -xzf /tmp/cmake.tar.gz -C /tmp && \ + cd /tmp/cmake-3.22.6 && \ + ./bootstrap && \ + make -j$(nproc) && \ + make install && \ + rm -rf /tmp/cmake.tar.gz /tmp/cmake-3.22.6 + ARG CORTEX_CPP_VERSION=latest ARG CMAKE_EXTRA_FLAGS="" diff --git a/docker/Dockerfile.cache b/docker/Dockerfile.cache index 0a9cbe02d..3eabc5dce 100644 --- a/docker/Dockerfile.cache +++ b/docker/Dockerfile.cache @@ -24,7 +24,6 @@ RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/nul apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" && \ apt-get update && \ apt-get install -y --no-install-recommends \ - cmake \ make \ git \ uuid-dev \ @@ -37,11 +36,21 @@ RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/nul ninja-build \ pkg-config \ python3-pip \ - openssl && \ + openssl \ + libssl-dev && \ pip3 install awscli && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* +# Download and install CMake 3.22.6 +RUN wget https://github.com/Kitware/CMake/releases/download/v3.22.6/cmake-3.22.6.tar.gz -q -O /tmp/cmake.tar.gz && \ + tar -xzf /tmp/cmake.tar.gz -C /tmp && \ + cd /tmp/cmake-3.22.6 && \ + ./bootstrap && \ + make -j$(nproc) && \ + make install && \ + rm -rf /tmp/cmake.tar.gz /tmp/cmake-3.22.6 + ARG CORTEX_CPP_VERSION=latest ARG CMAKE_EXTRA_FLAGS="" From 005e2684e743688df320264bba2bdc00aa99abf2 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Fri, 11 Apr 2025 10:59:07 +0700 Subject: [PATCH 38/49] fix: handle more parameters (#2199) * fix: handle more parameters * fix: generate version.txt if not exist * fix: only add path if exists --- engine/extensions/local-engine/local_engine.cc | 15 ++++++++++++++- engine/services/engine_service.cc | 12 ++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 885c14d77..b769c5e8c 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -20,7 +20,7 @@ const std::unordered_set kIgnoredParams = { "user_prompt", "min_keep", "mirostat", "mirostat_eta", "mirostat_tau", "text_model", "version", "n_probs", "object", "penalize_nl", "precision", "size", - "stop", "tfs_z", "typ_p"}; + "stop", "tfs_z", "typ_p", "caching_enabled"}; const std::unordered_map kParamsMap = { {"cpu_threads", "--threads"}, @@ -67,6 +67,19 @@ std::vector ConvertJsonToParamsVector(const Json::Value& root) { res.push_back("--embedding"); } continue; + } else if (member == "cache_type") { + if (!root[member].isNull()) { + res.push_back("-ctk"); + res.push_back(root[member].asString()); + res.push_back("-ctv"); + res.push_back(root[member].asString()); + } + continue; + } else if (member == "use_mmap") { + if (!root[member].asBool()) { + res.push_back("--no-mmap"); + } + continue; } res.push_back("--" + member); diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index 89cd00058..15c7148c7 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -772,7 +772,13 @@ EngineService::GetInstalledEngineVariants(const std::string& engine) const { // try to find version.txt auto version_txt_path = version_entry.path() / "version.txt"; if (!std::filesystem::exists(version_txt_path)) { - continue; + // create new one + std::ofstream meta(version_txt_path, std::ios::out); + meta << "name: " << entry.path().filename() << std::endl; + meta << "version: " << version_entry.path().filename() << std::endl; + meta.close(); + CTL_INF("name: " << entry.path().filename().string() << ", version: " + << version_entry.path().filename().string()); } try { @@ -865,7 +871,9 @@ void EngineService::RegisterEngineLibPath() { // register deps std::vector paths{}; - paths.push_back(cuda_path); + if (std::filesystem::exists(cuda_path)) { + paths.push_back(cuda_path); + } paths.push_back(engine_dir_path); CTL_DBG("Registering dylib for " From 7fda1868620c9e05e205ddde4f8e51fde5baaa71 Mon Sep 17 00:00:00 2001 From: hiento09 <136591877+hiento09@users.noreply.github.com> Date: Fri, 11 Apr 2025 11:10:41 +0700 Subject: [PATCH 39/49] chore: separate mac binary (#2198) --- .github/workflows/template-build-macos.yml | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/.github/workflows/template-build-macos.yml b/.github/workflows/template-build-macos.yml index 038546097..ea96d2df6 100644 --- a/.github/workflows/template-build-macos.yml +++ b/.github/workflows/template-build-macos.yml @@ -253,6 +253,14 @@ jobs: cd engine make codesign-binary CODE_SIGN=true DEVELOPER_ID="${{ secrets.DEVELOPER_ID }}" DESTINATION_BINARY_NAME="${{ steps.set-output-params.outputs.destination_binary_name }}" DESTINATION_BINARY_SERVER_NAME="${{ steps.set-output-params.outputs.destination_binary_server_name }}" + - name: Code Signing binaries for separate binary + run: | + codesign --force -s "${{ secrets.DEVELOPER_ID }}" --options=runtime --entitlements="./engine/templates/macos/entitlements.plist" ./cortex-${{ inputs.new_version }}-mac-arm64/${{ steps.set-output-params.outputs.destination_binary_name }} + codesign --force -s "${{ secrets.DEVELOPER_ID }}" --options=runtime --entitlements="./engine/templates/macos/entitlements.plist" ./cortex-${{ inputs.new_version }}-mac-arm64/${{ steps.set-output-params.outputs.destination_binary_server_name }} + + codesign --force -s "${{ secrets.DEVELOPER_ID }}" --options=runtime --entitlements="./engine/templates/macos/entitlements.plist" ./cortex-${{ inputs.new_version }}-mac-amd64/${{ steps.set-output-params.outputs.destination_binary_name }} + codesign --force -s "${{ secrets.DEVELOPER_ID }}" --options=runtime --entitlements="./engine/templates/macos/entitlements.plist" ./cortex-${{ inputs.new_version }}-mac-amd64/${{ steps.set-output-params.outputs.destination_binary_server_name }} + - name: Notary macOS Binary run: | curl -sSfL https://raw.githubusercontent.com/anchore/quill/main/install.sh | sh -s -- -b /usr/local/bin @@ -265,6 +273,18 @@ jobs: QUILL_NOTARY_ISSUER: ${{ secrets.NOTARY_ISSUER }} QUILL_NOTARY_KEY: "/tmp/notary-key.p8" + - name: Notary macOS Binary for separate binary + run: | + # Notarize the binary + quill notarize ./cortex-${{ inputs.new_version }}-mac-arm64/${{ steps.set-output-params.outputs.destination_binary_name }} + quill notarize ./cortex-${{ inputs.new_version }}-mac-arm64/${{ steps.set-output-params.outputs.destination_binary_server_name }} + quill notarize ./cortex-${{ inputs.new_version }}-mac-amd64/${{ steps.set-output-params.outputs.destination_binary_name }} + quill notarize ./cortex-${{ inputs.new_version }}-mac-amd64/${{ steps.set-output-params.outputs.destination_binary_server_name }} + env: + QUILL_NOTARY_KEY_ID: ${{ secrets.NOTARY_KEY_ID }} + QUILL_NOTARY_ISSUER: ${{ secrets.NOTARY_ISSUER }} + QUILL_NOTARY_KEY: "/tmp/notary-key.p8" + - name: Build network Installers shell: bash run: | @@ -310,6 +330,24 @@ jobs: xcrun notarytool submit ${{ steps.set-output-params.outputs.package_name }}-local.pkg --apple-id ${{ secrets.APPLE_ID }} --password ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} --team-id ${{ secrets.APPLE_TEAM_ID }} --wait - name: Package + run: | + mkdir temp + # Mac arm64 + mv cortex-${{ inputs.new_version }}-mac-arm64 temp/cortex + cd temp + tar -czvf cortex-arm64.tar.gz cortex + mv cortex-arm64.tar.gz ../cortex-arm64.tar.gz + cd .. + rm -rf temp/cortex + + # Mac amd64 + mv cortex-${{ inputs.new_version }}-mac-amd64 temp/cortex + cd temp + tar -czvf cortex-amd64.tar.gz cortex + mv cortex-amd64.tar.gz ../cortex-amd64.tar.gz + cd .. + + - name: Package for separate binary run: | cd engine make package @@ -320,6 +358,18 @@ jobs: name: cortex-${{ inputs.new_version }}-mac-universal path: ./engine/cortex + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: cortex-${{ inputs.new_version }}-mac-arm64-signed + path: ./cortex-${{ inputs.new_version }}-mac-arm64 + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: cortex-${{ inputs.new_version }}-mac-amd64-signed + path: ./cortex-${{ inputs.new_version }}-mac-amd64 + - name: Upload Artifact uses: actions/upload-artifact@v4 with: @@ -358,6 +408,28 @@ jobs: asset_name: cortex-${{ inputs.new_version }}-mac-universal.tar.gz asset_content_type: application/zip + - name: Upload release assert if public provider is github + if: inputs.public_provider == 'github' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: actions/upload-release-asset@v1.0.1 + with: + upload_url: ${{ inputs.upload_url }} + asset_path: ./cortex-arm64.tar.gz + asset_name: cortex-${{ inputs.new_version }}-mac-arm64.tar.gz + asset_content_type: application/zip + + - name: Upload release assert if public provider is github + if: inputs.public_provider == 'github' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: actions/upload-release-asset@v1.0.1 + with: + upload_url: ${{ inputs.upload_url }} + asset_path: ./cortex-amd64.tar.gz + asset_name: cortex-${{ inputs.new_version }}-mac-amd64.tar.gz + asset_content_type: application/zip + - name: Upload release assert if public provider is github if: inputs.public_provider == 'github' env: From 3d74607cdd7abf431ed2c78fbb15760c4f786591 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Tue, 6 May 2025 00:42:08 -0700 Subject: [PATCH 40/49] chore: function calling cleanup (#2195) * chore: function calling cleanup * chore: cleanup --------- Co-authored-by: sangjanai --- docs/docs/guides/function-calling.md | 30 ++- engine/controllers/server.cc | 1 - .../extensions/local-engine/local_engine.cc | 1 + engine/services/inference_service.cc | 47 ----- engine/services/model_service.cc | 26 +-- engine/services/model_service.h | 9 - .../test/components/test_function_calling.cc | 157 ---------------- engine/utils/cli_selection_utils.h | 26 +-- engine/utils/function_calling/common.h | 153 ---------------- function-calling.py | 173 ++++++++++++++++++ 10 files changed, 214 insertions(+), 409 deletions(-) delete mode 100644 engine/test/components/test_function_calling.cc create mode 100644 function-calling.py diff --git a/docs/docs/guides/function-calling.md b/docs/docs/guides/function-calling.md index 6b9157f18..7725f225d 100644 --- a/docs/docs/guides/function-calling.md +++ b/docs/docs/guides/function-calling.md @@ -63,8 +63,14 @@ tools = [ completion_payload = { "messages": [ - {"role": "system", "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."}, - {"role": "user", "content": "Hi, can you tell me the delivery date for my order?"}, + { + "role": "system", + "content": 'You have access to the following CUSTOM functions:\n\n\n\nIf a you choose to call a function ONLY reply in the following format:\n<{start_tag}={function_name}>{parameters}{end_tag}\nwhere\n\nstart_tag => ` a JSON dict with the function argument name as key and function argument value as value.\nend_tag => ``\n\nHere is an example,\n{"example_name": "example_value"}\n\nReminder:\n- Function calls MUST follow the specified format\n- Required parameters MUST be specified\n- You can call one or more functions at a time, but remember only chose correct function\n- Put the entire function call reply on one line\n- Always add your sources when using search results to answer the user query\n- If you can not find correct parameters or arguments corresponding to function in the user\'s message, ask user again to provide, do not make assumptions.\n- No explanation are needed when calling a function.\n\nYou are a helpful assistant.', + }, + { + "role": "user", + "content": "Hi, can you tell me the delivery date for my order?" + }, ] } @@ -126,10 +132,22 @@ Once the user provides their order ID: ```python completion_payload = { "messages": [ - {"role": "system", "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."}, - {"role": "user", "content": "Hi, can you tell me the delivery date for my order?"}, - {"role": "assistant", "content": "Of course! Please provide your order ID so I can look it up."}, - {"role": "user", "content": "i think it is order_70705"}, + { + "role": "system", + "content": 'You have access to the following CUSTOM functions:\n\n\n\nIf a you choose to call a function ONLY reply in the following format:\n<{start_tag}={function_name}>{parameters}{end_tag}\nwhere\n\nstart_tag => ` a JSON dict with the function argument name as key and function argument value as value.\nend_tag => ``\n\nHere is an example,\n{"example_name": "example_value"}\n\nReminder:\n- Function calls MUST follow the specified format\n- Required parameters MUST be specified\n- You can call one or more functions at a time, but remember only chose correct function\n- Put the entire function call reply on one line\n- Always add your sources when using search results to answer the user query\n- If you can not find correct parameters or arguments corresponding to function in the user\'s message, ask user again to provide, do not make assumptions.\n- No explanation are needed when calling a function.\n\nYou are a helpful assistant.', + }, + { + "role": "user", + "content": "Hi, can you tell me the delivery date for my order?" + }, + { + "role": "assistant", + "content": "Of course! Please provide your order ID so I can look it up." + }, + { + "role": "user", + "content": "i think it is order_70705" + }, ] } diff --git a/engine/controllers/server.cc b/engine/controllers/server.cc index 6ea733a70..3ba4aa327 100644 --- a/engine/controllers/server.cc +++ b/engine/controllers/server.cc @@ -179,7 +179,6 @@ void server::ProcessStreamRes(std::function cb, void server::ProcessNonStreamRes(std::function cb, SyncQueue& q) { auto [status, res] = q.wait_and_pop(); - function_calling_utils::PostProcessResponse(res); LOG_DEBUG << "response: " << res.toStyledString(); auto resp = cortex_utils::CreateCortexHttpJsonResponse(res); resp->setStatusCode( diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index b769c5e8c..c4d296427 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -544,6 +544,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, params.push_back("--pooling"); params.push_back("mean"); + params.push_back("--jinja"); std::vector v; v.reserve(params.size() + 1); diff --git a/engine/services/inference_service.cc b/engine/services/inference_service.cc index 75d95f06d..e07ed71ba 100644 --- a/engine/services/inference_service.cc +++ b/engine/services/inference_service.cc @@ -13,8 +13,6 @@ cpp::result InferenceService::HandleChatCompletion( engine_type = (*(json_body)).get("engine", kLlamaRepo).asString(); } CTL_DBG("engine_type: " << engine_type); - function_calling_utils::PreprocessRequest(json_body); - CTL_DBG("engine_type: " << engine_type); auto tool_choice = json_body->get("tool_choice", Json::Value::null); auto model_id = json_body->get("model", "").asString(); if (saved_models_.find(model_id) != saved_models_.end()) { @@ -46,51 +44,6 @@ cpp::result InferenceService::HandleChatCompletion( return cpp::fail(std::make_pair(stt, res)); } - if (!model_id.empty()) { - if (auto model_service = model_service_.lock()) { - auto metadata_ptr = model_service->GetCachedModelMetadata(model_id); - if (metadata_ptr != nullptr && - !metadata_ptr->tokenizer->chat_template.empty()) { - auto tokenizer = metadata_ptr->tokenizer; - auto messages = (*json_body)["messages"]; - Json::Value messages_jsoncpp(Json::arrayValue); - for (auto message : messages) { - messages_jsoncpp.append(message); - } - - Json::Value tools(Json::arrayValue); - Json::Value template_data_json; - template_data_json["messages"] = messages_jsoncpp; - // template_data_json["tools"] = tools; - - auto prompt_result = jinja::RenderTemplate( - tokenizer->chat_template, template_data_json, tokenizer->bos_token, - tokenizer->eos_token, tokenizer->add_bos_token, - tokenizer->add_eos_token, tokenizer->add_generation_prompt); - if (prompt_result.has_value()) { - (*json_body)["prompt"] = prompt_result.value(); - if (json_body->isMember("stop")) { - bool need_append = true; - for (auto& s : (*json_body)["stop"]) { - if (s.asString() == tokenizer->eos_token) { - need_append = false; - } - } - if (need_append) { - (*json_body)["stop"].append(tokenizer->eos_token); - } - } else { - Json::Value stops(Json::arrayValue); - stops.append(tokenizer->eos_token); - (*json_body)["stop"] = stops; - } - } else { - CTL_ERR("Failed to render prompt: " + prompt_result.error()); - } - } - } - } - CTL_DBG("Json body inference: " + json_body->toStyledString()); auto cb = [q, tool_choice](Json::Value status, Json::Value res) { diff --git a/engine/services/model_service.cc b/engine/services/model_service.cc index d9359b698..68f0fe070 100644 --- a/engine/services/model_service.cc +++ b/engine/services/model_service.cc @@ -691,21 +691,7 @@ cpp::result ModelService::StartModel( auto status = std::get<0>(ir)["status_code"].asInt(); auto data = std::get<1>(ir); - if (status == drogon::k200OK) { - // start model successfully, in case not vision model, we store the metadata so we can use - // for each inference - if (!json_data.isMember("mmproj") || json_data["mmproj"].isNull()) { - auto metadata_res = GetModelMetadata(model_handle); - if (metadata_res.has_value()) { - loaded_model_metadata_map_.emplace(model_handle, - std::move(metadata_res.value())); - CTL_INF("Successfully stored metadata for model " << model_handle); - } else { - CTL_WRN("Failed to get metadata for model " << model_handle << ": " - << metadata_res.error()); - } - } - + if (status == drogon::k200OK) { return StartModelResult{/* .success = */ true, /* .warning = */ may_fallback_res.value()}; } else if (status == drogon::k409Conflict) { @@ -760,8 +746,6 @@ cpp::result ModelService::StopModel( if (bypass_check) { bypass_stop_check_set_.erase(model_handle); } - loaded_model_metadata_map_.erase(model_handle); - CTL_INF("Removed metadata for model " << model_handle); return true; } else { CTL_ERR("Model failed to stop with status code: " << status); @@ -1090,14 +1074,6 @@ ModelService::GetModelMetadata(const std::string& model_id) const { return std::move(*model_metadata_res); } -std::shared_ptr ModelService::GetCachedModelMetadata( - const std::string& model_id) const { - if (loaded_model_metadata_map_.find(model_id) == - loaded_model_metadata_map_.end()) - return nullptr; - return loaded_model_metadata_map_.at(model_id); -} - std::string ModelService::GetEngineByModelId( const std::string& model_id) const { namespace fs = std::filesystem; diff --git a/engine/services/model_service.h b/engine/services/model_service.h index beba91f8c..fa247b954 100644 --- a/engine/services/model_service.h +++ b/engine/services/model_service.h @@ -83,9 +83,6 @@ class ModelService { cpp::result, std::string> GetModelMetadata( const std::string& model_id) const; - std::shared_ptr GetCachedModelMetadata( - const std::string& model_id) const; - std::string GetEngineByModelId(const std::string& model_id) const; private: @@ -104,12 +101,6 @@ class ModelService { std::unordered_set bypass_stop_check_set_; std::shared_ptr engine_svc_ = nullptr; - /** - * Store the chat template of loaded model. - */ - std::unordered_map> - loaded_model_metadata_map_; - std::mutex es_mtx_; std::unordered_map> es_; cortex::TaskQueue& task_queue_; diff --git a/engine/test/components/test_function_calling.cc b/engine/test/components/test_function_calling.cc deleted file mode 100644 index 7a4810b29..000000000 --- a/engine/test/components/test_function_calling.cc +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include "gtest/gtest.h" -#include "json/json.h" -#include "utils/function_calling/common.h" - -class FunctionCallingUtilsTest : public ::testing::Test { - protected: - std::shared_ptr createTestRequest() { - auto request = std::make_shared(); - (*request)["tools"] = Json::Value(Json::arrayValue); - return request; - } -}; - -TEST_F(FunctionCallingUtilsTest, ReplaceCustomFunctions) { - std::string original = "Test placeholder"; - std::string replacement = "Custom function"; - std::string result = - function_calling_utils::ReplaceCustomFunctions(original, replacement); - EXPECT_EQ(result, "Test Custom function placeholder"); -} - -TEST_F(FunctionCallingUtilsTest, HasTools) { - auto request = createTestRequest(); - EXPECT_FALSE(function_calling_utils::HasTools(request)); - - (*request)["tools"].append(Json::Value()); - EXPECT_TRUE(function_calling_utils::HasTools(request)); - - (*request)["tools"] = "random"; - EXPECT_FALSE(function_calling_utils::HasTools(request)); - - (*request)["tools"] = Json::Value::null; - EXPECT_FALSE(function_calling_utils::HasTools(request)); -} - -TEST_F(FunctionCallingUtilsTest, ProcessTools) { - auto request = createTestRequest(); - Json::Value tool; - tool["type"] = "function"; - tool["function"]["name"] = "test_function"; - tool["function"]["description"] = "Test description"; - (*request)["tools"].append(tool); - - std::string result = function_calling_utils::ProcessTools(request); - EXPECT_TRUE( - result.find("Use the function 'test_function' to: Test description") != - std::string::npos); -} - -TEST_F(FunctionCallingUtilsTest, ParseMultipleFunctionStrings) { - std::string input = - "{\"arg\":\"value1\"}{\"arg\":\"value2\"}"; - Json::Value result = - function_calling_utils::ParseMultipleFunctionStrings(input); - - ASSERT_EQ(result.size(), 2); - EXPECT_EQ(result[0]["function"]["name"].asString(), "func1"); - EXPECT_EQ(result[0]["function"]["arguments"].asString(), - "{\"arg\":\"value1\"}"); - EXPECT_EQ(result[1]["function"]["name"].asString(), "func2"); - EXPECT_EQ(result[1]["function"]["arguments"].asString(), - "{\"arg\":\"value2\"}"); -} - -TEST_F(FunctionCallingUtilsTest, ConvertJsonToFunctionStrings) { - Json::Value jsonArray(Json::arrayValue); - Json::Value function1, function2; - function1["function"]["name"] = "func1"; - function1["function"]["arguments"] = "{\"arg\":\"value1\"}"; - function2["function"]["name"] = "func2"; - function2["function"]["arguments"] = "{\"arg\":\"value2\"}"; - jsonArray.append(function1); - jsonArray.append(function2); - - std::string result = - function_calling_utils::ConvertJsonToFunctionStrings(jsonArray); - EXPECT_EQ(result, - "{\"arg\":\"value1\"}{\"arg\":\"value2\"}"); -} - -TEST_F(FunctionCallingUtilsTest, CreateCustomFunctionsString) { - auto request = createTestRequest(); - Json::Value tool; - tool["type"] = "function"; - tool["function"]["name"] = "test_function"; - tool["function"]["description"] = "Test description"; - (*request)["tools"].append(tool); - - std::string result = - function_calling_utils::CreateCustomFunctionsString(request); - EXPECT_TRUE(result.find("```") != std::string::npos); - EXPECT_TRUE( - result.find("Use the function 'test_function' to: Test description") != - std::string::npos); -} - -TEST_F(FunctionCallingUtilsTest, IsValidToolChoiceFormat) { - Json::Value validTool; - validTool["type"] = "function"; - validTool["function"]["name"] = "test_function"; - EXPECT_TRUE(function_calling_utils::IsValidToolChoiceFormat(validTool)); - - Json::Value invalidTool; - EXPECT_FALSE(function_calling_utils::IsValidToolChoiceFormat(invalidTool)); -} - -TEST_F(FunctionCallingUtilsTest, UpdateMessages) { - auto request = createTestRequest(); - std::string system_prompt = "Original prompt"; - (*request)["messages"] = Json::Value(Json::arrayValue); - - function_calling_utils::UpdateMessages(system_prompt, request); - - ASSERT_TRUE((*request)["messages"].isArray()); - EXPECT_EQ((*request)["messages"][0]["role"].asString(), "system"); - EXPECT_EQ((*request)["messages"][0]["content"].asString(), system_prompt); -} - -TEST_F(FunctionCallingUtilsTest, PreprocessRequest) { - auto request = createTestRequest(); - Json::Value tool; - tool["type"] = "function"; - tool["function"]["name"] = "test_function"; - tool["function"]["description"] = "Test description"; - (*request)["tools"].append(tool); - - function_calling_utils::PreprocessRequest(request); - - ASSERT_TRUE((*request)["messages"].isArray()); - EXPECT_TRUE((*request)["messages"][0]["content"].asString().find( - "Test description") != std::string::npos); -} - -TEST_F(FunctionCallingUtilsTest, PostProcessResponse) { - Json::Value response; - response["choices"] = Json::Value(Json::arrayValue); - Json::Value choice; - choice["message"]["content"] = - "{\"arg\":\"value\"}"; - response["choices"].append(choice); - - function_calling_utils::PostProcessResponse(response); - - EXPECT_EQ(response["choices"][0]["message"]["content"].asString(), ""); - EXPECT_TRUE(response["choices"][0]["message"]["tool_calls"].isArray()); - EXPECT_EQ( - response["choices"][0]["message"]["tool_calls"][0]["function"]["name"] - .asString(), - "test_function"); - EXPECT_EQ(response["choices"][0]["message"]["tool_calls"][0]["function"] - ["arguments"] - .asString(), - "{\"arg\":\"value\"}"); -} \ No newline at end of file diff --git a/engine/utils/cli_selection_utils.h b/engine/utils/cli_selection_utils.h index dca6fe675..487c21e6b 100644 --- a/engine/utils/cli_selection_utils.h +++ b/engine/utils/cli_selection_utils.h @@ -27,13 +27,13 @@ inline void PrintMenu( inline std::optional GetNumericValue(const std::string& sval) { try { - return std::stoi(sval); + return std::stoi(sval); } catch (const std::invalid_argument&) { - // Not a valid number - return std::nullopt; + // Not a valid number + return std::nullopt; } catch (const std::out_of_range&) { - // Number out of range - return std::nullopt; + // Number out of range + return std::nullopt; } } @@ -73,14 +73,16 @@ inline std::optional PrintModelSelection( } // Validate if the selection consists solely of numeric characters - if(!std::all_of(selection.begin(), selection.end(), ::isdigit)){ + if (!std::all_of(selection.begin(), selection.end(), ::isdigit)) { return std::nullopt; } // deal with out of range numeric values std::optional numeric_value = GetNumericValue(selection); - - if (!numeric_value.has_value() || (unsigned) numeric_value.value() > availables.size() || numeric_value.value() < 1) { + + if (!numeric_value.has_value() || + (unsigned)numeric_value.value() > availables.size() || + numeric_value.value() < 1) { return std::nullopt; } @@ -101,13 +103,15 @@ inline std::optional PrintSelection( } // Validate if the selection consists solely of numeric characters - if(!std::all_of(selection.begin(), selection.end(), ::isdigit)){ + if (!std::all_of(selection.begin(), selection.end(), ::isdigit)) { return std::nullopt; } - + // deal with out of range numeric values std::optional numeric_value = GetNumericValue(selection); - if (!numeric_value.has_value() ||(unsigned) numeric_value.value() > options.size() || numeric_value.value() < 1) { + if (!numeric_value.has_value() || + (unsigned)numeric_value.value() > options.size() || + numeric_value.value() < 1) { return std::nullopt; } diff --git a/engine/utils/function_calling/common.h b/engine/utils/function_calling/common.h index 34a1c9862..953a9964c 100644 --- a/engine/utils/function_calling/common.h +++ b/engine/utils/function_calling/common.h @@ -129,157 +129,4 @@ inline Json::Value ParseJsonString(const std::string& jsonString) { return root; } -inline std::string CreateCustomFunctionsString( - std::shared_ptr request) { - std::string customFunctions = ProcessTools(request); - if (customFunctions.empty()) { - return ""; // No custom functions found - } - - return "```\n" + customFunctions + "```"; -} -inline bool IsValidToolChoiceFormat(const Json::Value& root) { - return root.isObject() && root.isMember("type") && root["type"].isString() && - root["type"].asString() == "function" && root.isMember("function") && - root["function"].isObject() && root["function"].isMember("name") && - root["function"]["name"].isString(); -} -inline void UpdateMessages(std::string& system_prompt, - std::shared_ptr request) { - Json::Value tool_choice = request->get("tool_choice", "auto"); - if (tool_choice.isString() && tool_choice.asString() == "required") { - system_prompt += - "\n\nYou must call a function to answer the user's question."; - } else if (!tool_choice.isString()) { - - system_prompt += - "\n\nNow this is your first priority: You must call the function '" + - tool_choice["function"]["name"].asString() + - "' to answer the user's question."; - } - bool parallel_tool_calls = request->get("parallel_tool_calls", true).asBool(); - if (!parallel_tool_calls) { - system_prompt += "\n\nNow this is your first priority: You must call the only one function at a time."; - } - - bool tools_call_in_user_message = - request->get("tools_call_in_user_message", false).asBool(); - - bool original_stream_config = (*request).get("stream", false).asBool(); - // (*request)["grammar"] = function_calling_utils::gamma_json; - (*request)["stream"] = - false; //when using function calling, disable stream automatically because we need to parse the response to get function name and params - - if (!request->isMember("messages") || !(*request)["messages"].isArray() || - (*request)["messages"].empty()) { - // If no messages, add the system prompt as the first message - Json::Value systemMessage; - systemMessage["role"] = "system"; - systemMessage["content"] = system_prompt; - (*request)["messages"].append(systemMessage); - } else { - - if (tools_call_in_user_message) { - for (Json::Value& message : (*request)["messages"]) { - if (message["role"] == "user" && message.isMember("tools") && - message["tools"].isArray() && message["tools"].size() > 0) { - message["content"] = system_prompt + "\n User question: " + - message["content"].asString(); - } - } - } else { - Json::Value& firstMessage = (*request)["messages"][0]; - if (firstMessage["role"] == "system") { - bool addCustomPrompt = - request->get("add_custom_system_prompt", true).asBool(); - if (addCustomPrompt) { - firstMessage["content"] = - system_prompt + "\n" + firstMessage["content"].asString(); - } - } else { - // If the first message is not a system message, prepend the system prompt - Json::Value systemMessage; - systemMessage["role"] = "system"; - systemMessage["content"] = system_prompt; - (*request)["messages"].insert(0, systemMessage); - } - } - - // transform last message role to tool if it is a function call - Json::Value& lastMessage = - (*request)["messages"][(*request)["messages"].size() - 1]; - if (lastMessage.get("role", "") == "tool") { - lastMessage["role"] = function_calling_llama3_1_utils::tool_role; - (*request)["stream"] = - original_stream_config; // if role is tool then should restore stream config to original value - } - } - for (Json::Value& message : (*request)["messages"]) { - if (message["role"] == "assistant" && message.isMember("tool_calls")) { - const Json::Value& tool_calls = message["tool_calls"]; - if (!tool_calls.isNull() && tool_calls.isArray() && - tool_calls.size() > 0) { - message["content"] = ConvertJsonToFunctionStrings(tool_calls); - message["tool_calls"] = {}; - } - } - } -} -inline void PreprocessRequest(std::shared_ptr request) { - if (!function_calling_utils::HasTools(request)) { - return; // Exit if no tools present - } - if (request->get("tool_choice", "auto").isString()) { - std::string tool_choice = request->get("tool_choice", "auto").asString(); - if (tool_choice == "none") { - return; // Exit if tool_choice is none - } - } - std::string customFunctionsString = - function_calling_utils::CreateCustomFunctionsString(request); - std::string new_system_prompt = - function_calling_utils::ReplaceCustomFunctions( - function_calling_llama3_1_utils::system_prompt, - customFunctionsString); - UpdateMessages(new_system_prompt, request); -} - -inline void PostProcessResponse(Json::Value& response) { - if (!response.isMember("choices") || !response["choices"].isArray() || - response["choices"].empty()) { - // If there are no choices or the structure is incorrect, do nothing - return; - } - - // Get a reference to the first choice - Json::Value& firstChoice = response["choices"][0]; - - // Check if the choice has a message with content - if (firstChoice.isMember("message") && - firstChoice["message"].isMember("content")) { - std::string content = firstChoice["message"]["content"].asString(); - - // Create a new structure for tool_calls - Json::Value toolCall = ParseMultipleFunctionStrings(content); - if (toolCall.size() > 0) { - // Add tool_calls to the message - if (response.get("tool_choice", "auto").isString()) { - std::string tool_choice = - response.get("tool_choice", "auto").asString(); - if (tool_choice == "auto") { - firstChoice["finish_reason"] = "tool_calls"; - } else { - firstChoice["finish_reason"] = "stop"; - } - } - - firstChoice["message"]["tool_calls"] = toolCall; - - // Clear the content as it's now represented in tool_calls - firstChoice["message"]["content"] = ""; - } - } - - // Add any additional post-processing logic here -} } // namespace function_calling_utils diff --git a/function-calling.py b/function-calling.py new file mode 100644 index 000000000..32ef31752 --- /dev/null +++ b/function-calling.py @@ -0,0 +1,173 @@ +from datetime import datetime +from openai import OpenAI +from pydantic import BaseModel +import json + +# MODEL = "deepseek-r1-distill-qwen-7b:7b" +MODEL = "llama3.1:8b-q8" + +client = OpenAI( + base_url="http://localhost:39281/v1", + api_key="not-needed", # Authentication is not required for local deployment +) + +tools = [ + { + "type": "function", + "function": { + "name": "puppeteer_navigate", + "description": "Navigate to a URL", + "parameters": { + "properties": {"url": {"type": "string"}}, + "required": ["url"], + "type": "object", + }, + "strict": False, + }, + }, + { + "type": "function", + "function": { + "name": "puppeteer_screenshot", + "description": "Take a screenshot of the current page or a specific element", + "parameters": { + "properties": { + "height": { + "description": "Height in pixels (default: 600)", + "type": "number", + }, + "name": { + "description": "Name for the screenshot", + "type": "string", + }, + "selector": { + "description": "CSS selector for element to screenshot", + "type": "string", + }, + "width": { + "description": "Width in pixels (default: 800)", + "type": "number", + }, + }, + "required": ["name"], + "type": "object", + }, + "strict": False, + }, + }, + { + "type": "function", + "function": { + "name": "puppeteer_click", + "description": "Click an element on the page", + "parameters": { + "properties": { + "selector": { + "description": "CSS selector for element to click", + "type": "string", + } + }, + "required": ["selector"], + "type": "object", + }, + "strict": False, + }, + }, + { + "type": "function", + "function": { + "name": "puppeteer_fill", + "description": "Fill out an input field", + "parameters": { + "properties": { + "selector": { + "description": "CSS selector for input field", + "type": "string", + }, + "value": {"description": "Value to fill", "type": "string"}, + }, + "required": ["selector", "value"], + "type": "object", + }, + "strict": False, + }, + }, + { + "type": "function", + "function": { + "name": "puppeteer_select", + "description": "Select an element on the page with Select tag", + "parameters": { + "properties": { + "selector": { + "description": "CSS selector for element to select", + "type": "string", + }, + "value": {"description": "Value to select", "type": "string"}, + }, + "required": ["selector", "value"], + "type": "object", + }, + "strict": False, + }, + }, + { + "type": "function", + "function": { + "name": "puppeteer_hover", + "description": "Hover an element on the page", + "parameters": { + "properties": { + "selector": { + "description": "CSS selector for element to hover", + "type": "string", + } + }, + "required": ["selector"], + "type": "object", + }, + "strict": False, + }, + }, + { + "type": "function", + "function": { + "name": "puppeteer_evaluate", + "description": "Execute JavaScript in the browser console", + "parameters": { + "properties": { + "script": { + "description": "JavaScript code to execute", + "type": "string", + } + }, + "required": ["script"], + "type": "object", + }, + "strict": False, + }, + }, +] + +completion_payload = { + "messages": [ + { + "role": "system", + "content": 'You have access to the following CUSTOM functions:\n\n\n\nIf a you choose to call a function ONLY reply in the following format:\n<{start_tag}={function_name}>{parameters}{end_tag}\nwhere\n\nstart_tag => ` a JSON dict with the function argument name as key and function argument value as value.\nend_tag => ``\n\nHere is an example,\n{"example_name": "example_value"}\n\nReminder:\n- Function calls MUST follow the specified format\n- Required parameters MUST be specified\n- You can call one or more functions at a time, but remember only chose correct function\n- Put the entire function call reply on one line\n- Always add your sources when using search results to answer the user query\n- If you can not find correct parameters or arguments corresponding to function in the user\'s message, ask user again to provide, do not make assumptions.\n- No explanation are needed when calling a function.\n\nYou are a helpful assistant.', + }, + { + "role": "user", + "content": "go to google search", + }, + ] +} + +response = client.chat.completions.create( + top_p=0.9, + temperature=0.6, + model=MODEL, + messages=completion_payload["messages"], + tools=tools, +) + +print(response) \ No newline at end of file From f19228771d493f8fb8caee50cc4bfe55238ac306 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Tue, 13 May 2025 13:51:56 +0700 Subject: [PATCH 41/49] fix: remove jinja parameter (#2205) * fix: remove jinja parameter * chore: disable linux arm CI --------- Co-authored-by: sangjanai --- .github/workflows/beta-build.yml | 30 +++++++++---------- .github/workflows/cortex-cpp-quality-gate.yml | 24 +++++++-------- .github/workflows/nightly-build.yml | 28 ++++++++--------- .github/workflows/stable-build.yml | 28 ++++++++--------- .../extensions/local-engine/local_engine.cc | 1 - 5 files changed, 55 insertions(+), 56 deletions(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index 1d5480312..64d4e28e7 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -81,20 +81,20 @@ jobs: llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: amd64 - build-linux-arm64: - uses: ./.github/workflows/template-build-linux.yml - secrets: inherit - needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] - with: - ref: ${{ github.ref }} - public_provider: github - new_version: ${{ needs.get-update-version.outputs.new_version }} - runs-on: ubuntu-2004-arm64 - cmake-flags: "-DCORTEX_VARIANT=beta -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" - channel: beta - upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} - arch: arm64 + # build-linux-arm64: + # uses: ./.github/workflows/template-build-linux.yml + # secrets: inherit + # needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] + # with: + # ref: ${{ github.ref }} + # public_provider: github + # new_version: ${{ needs.get-update-version.outputs.new_version }} + # runs-on: ubuntu-2004-arm64 + # cmake-flags: "-DCORTEX_VARIANT=beta -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" + # channel: beta + # upload_url: ${{ needs.create-draft-release.outputs.upload_url }} + # llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} + # arch: arm64 build-docker-x64: uses: ./.github/workflows/template-build-docker-x64.yml @@ -127,7 +127,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} noti-discord: - needs: [get-update-version, create-draft-release, build-macos, build-windows-x64, build-linux-x64, build-linux-arm64, update_release] + needs: [get-update-version, create-draft-release, build-macos, build-windows-x64, build-linux-x64, update_release] runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/cortex-cpp-quality-gate.yml b/.github/workflows/cortex-cpp-quality-gate.yml index fc2d52b63..02774d159 100644 --- a/.github/workflows/cortex-cpp-quality-gate.yml +++ b/.github/workflows/cortex-cpp-quality-gate.yml @@ -21,12 +21,12 @@ jobs: fail-fast: false matrix: include: - - os: "linux" - name: "arm64" - runs-on: "ubuntu-2004-arm64" - cmake-flags: "-DCORTEX_CPP_VERSION=${{github.event.pull_request.head.sha}} -DCMAKE_BUILD_TEST=ON -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" - build-deps-cmake-flags: "" - ccache-dir: "" + # - os: "linux" + # name: "arm64" + # runs-on: "ubuntu-2004-arm64" + # cmake-flags: "-DCORTEX_CPP_VERSION=${{github.event.pull_request.head.sha}} -DCMAKE_BUILD_TEST=ON -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" + # build-deps-cmake-flags: "" + # ccache-dir: "" - os: "linux" name: "amd64" runs-on: "ubuntu-20-04-cuda-12-0" @@ -354,12 +354,12 @@ jobs: fail-fast: false matrix: include: - - os: "linux" - name: "arm64" - runs-on: "ubuntu-2004-arm64" - cmake-flags: "-DCORTEX_CPP_VERSION=${{github.event.pull_request.head.sha}} -DCMAKE_BUILD_TEST=ON -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" - build-deps-cmake-flags: "" - ccache-dir: "" + # - os: "linux" + # name: "arm64" + # runs-on: "ubuntu-2004-arm64" + # cmake-flags: "-DCORTEX_CPP_VERSION=${{github.event.pull_request.head.sha}} -DCMAKE_BUILD_TEST=ON -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake" + # build-deps-cmake-flags: "" + # ccache-dir: "" - os: "linux" name: "amd64" runs-on: "ubuntu-20-04-cuda-12-0" diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index efdbfdf6f..f013a90e2 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -87,24 +87,24 @@ jobs: llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: amd64 - build-linux-arm64: - uses: ./.github/workflows/template-build-linux.yml - secrets: inherit - needs: [get-update-version, set-public-provider, get-llamacpp-latest-version] - with: - ref: ${{ needs.set-public-provider.outputs.ref }} - public_provider: ${{ needs.set-public-provider.outputs.public_provider }} - new_version: ${{ needs.get-update-version.outputs.new_version }} - runs-on: ubuntu-2004-arm64 - cmake-flags: "-DCORTEX_VARIANT=nightly -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" - channel: nightly - llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} - arch: arm64 + # build-linux-arm64: + # uses: ./.github/workflows/template-build-linux.yml + # secrets: inherit + # needs: [get-update-version, set-public-provider, get-llamacpp-latest-version] + # with: + # ref: ${{ needs.set-public-provider.outputs.ref }} + # public_provider: ${{ needs.set-public-provider.outputs.public_provider }} + # new_version: ${{ needs.get-update-version.outputs.new_version }} + # runs-on: ubuntu-2004-arm64 + # cmake-flags: "-DCORTEX_VARIANT=nightly -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" + # channel: nightly + # llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} + # arch: arm64 update-latest-version: runs-on: ubuntu-latest if: needs.set-public-provider.outputs.public_provider == 'aws-s3' - needs: [get-update-version, set-public-provider, build-linux-x64, build-linux-arm64, build-macos, build-windows-x64, get-llamacpp-latest-version] + needs: [get-update-version, set-public-provider, build-linux-x64, build-macos, build-windows-x64, get-llamacpp-latest-version] steps: - name: Update latest version id: update-latest-version diff --git a/.github/workflows/stable-build.yml b/.github/workflows/stable-build.yml index c4b5f53f3..27e05f9ce 100644 --- a/.github/workflows/stable-build.yml +++ b/.github/workflows/stable-build.yml @@ -81,20 +81,20 @@ jobs: llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} arch: amd64 - build-linux-arm64: - uses: ./.github/workflows/template-build-linux.yml - secrets: inherit - needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] - with: - ref: ${{ github.ref }} - public_provider: github - new_version: ${{ needs.get-update-version.outputs.new_version }} - runs-on: ubuntu-2004-arm64 - cmake-flags: "-DCORTEX_VARIANT=prod -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" - channel: stable - upload_url: ${{ needs.create-draft-release.outputs.upload_url }} - llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} - arch: arm64 + # build-linux-arm64: + # uses: ./.github/workflows/template-build-linux.yml + # secrets: inherit + # needs: [get-update-version, create-draft-release, get-llamacpp-latest-version] + # with: + # ref: ${{ github.ref }} + # public_provider: github + # new_version: ${{ needs.get-update-version.outputs.new_version }} + # runs-on: ubuntu-2004-arm64 + # cmake-flags: "-DCORTEX_VARIANT=prod -DCORTEX_CPP_VERSION='v${{ needs.get-update-version.outputs.new_version }}' -DCMAKE_TOOLCHAIN_FILE=/home/runner/actions-runner/_work/cortex.cpp/cortex.cpp/engine/vcpkg/scripts/buildsystems/vcpkg.cmake" + # channel: stable + # upload_url: ${{ needs.create-draft-release.outputs.upload_url }} + # llamacpp-version: ${{ needs.get-llamacpp-latest-version.outputs.llamacpp_latest_version }} + # arch: arm64 build-docker-x64: uses: ./.github/workflows/template-build-docker-x64.yml diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index c4d296427..b769c5e8c 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -544,7 +544,6 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, params.push_back("--pooling"); params.push_back("mean"); - params.push_back("--jinja"); std::vector v; v.reserve(params.size() + 1); From a90a5e8687801a7cdcd797eb6f9e8c140df91d76 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Fri, 16 May 2025 07:11:48 +0700 Subject: [PATCH 42/49] fix: check model is loaded before starting (#2206) * fix: check model is loaded before starting * chore: e2e test --------- Co-authored-by: sangjanai --- engine/config/yaml_config.cc | 2 +- .../cli/engines/test_cli_engine_uninstall.py | 5 +++- engine/e2e-test/cli/model/test_cli_model.py | 1 + .../extensions/local-engine/local_engine.cc | 29 ++++++++++++++++--- engine/services/model_source_service.cc | 3 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/engine/config/yaml_config.cc b/engine/config/yaml_config.cc index 9650ffdcc..38128e1c4 100644 --- a/engine/config/yaml_config.cc +++ b/engine/config/yaml_config.cc @@ -48,7 +48,7 @@ void YamlHandler::ReadYamlFile(const std::string& file_path) { if (!yaml_node_["mmproj"]) { auto s = nomalize_path(file_path); auto abs_path = s.substr(0, s.find_last_of('/')) + "/mmproj.gguf"; - CTL_DBG("mmproj: " << abs_path); + CTL_TRC("mmproj: " << abs_path); auto rel_path = fmu::ToRelativeCortexDataPath(fs::path(abs_path)); if (std::filesystem::exists(abs_path)) { yaml_node_["mmproj"] = rel_path.string(); diff --git a/engine/e2e-test/cli/engines/test_cli_engine_uninstall.py b/engine/e2e-test/cli/engines/test_cli_engine_uninstall.py index 8672110e2..3198c81a5 100644 --- a/engine/e2e-test/cli/engines/test_cli_engine_uninstall.py +++ b/engine/e2e-test/cli/engines/test_cli_engine_uninstall.py @@ -24,7 +24,10 @@ def setup_and_teardown(self): @pytest.mark.asyncio async def test_engines_uninstall_llamacpp_should_be_successfully(self): - response = requests.post("http://localhost:3928/v1/engines/llama-cpp/install") + data = {"version": "b5371"} + response = requests.post( + "http://localhost:3928/v1/engines/llama-cpp/install", json=data + ) await wait_for_websocket_download_success_event(timeout=None) exit_code, output, error = run( "Uninstall engine", ["engines", "uninstall", "llama-cpp"] diff --git a/engine/e2e-test/cli/model/test_cli_model.py b/engine/e2e-test/cli/model/test_cli_model.py index aa6e99e4a..cd80a9e2b 100644 --- a/engine/e2e-test/cli/model/test_cli_model.py +++ b/engine/e2e-test/cli/model/test_cli_model.py @@ -36,6 +36,7 @@ def setup_and_teardown(self): run("Delete model", ["models", "delete", "tinyllama:1b"]) stop_server() + @pytest.mark.skipif(platform.system() == "Windows", reason="Skip test for Windows") def test_model_pull_with_direct_url_should_be_success(self): exit_code, output, error = run( "Pull model", diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index b769c5e8c..2bba11a7b 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -80,6 +80,11 @@ std::vector ConvertJsonToParamsVector(const Json::Value& root) { res.push_back("--no-mmap"); } continue; + } else if (member == "ignore_eos") { + if (root[member].asBool()) { + res.push_back("--ignore_eos"); + } + continue; } res.push_back("--" + member); @@ -502,6 +507,23 @@ void LocalEngine::HandleEmbedding(std::shared_ptr json_body, void LocalEngine::LoadModel(std::shared_ptr json_body, http_callback&& callback) { + auto model_id = json_body->get("model", "").asString(); + if (model_id.empty()) { + CTL_WRN("Model is empty"); + } + if (server_map_.find(model_id) != server_map_.end()) { + CTL_INF("Model " << model_id << " is already loaded"); + Json::Value error; + error["error"] = "Model " + model_id + " is already loaded"; + Json::Value status; + status["is_done"] = true; + status["has_error"] = true; + status["is_stream"] = false; + status["status_code"] = 409; + callback(std::move(status), std::move(error)); + return; + } + CTL_INF("Start loading model"); auto wait_for_server_up = [this](const std::string& model, const std::string& host, int port) { @@ -524,10 +546,7 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, }; LOG_DEBUG << "Start to spawn llama-server"; - auto model_id = json_body->get("model", "").asString(); - if (model_id.empty()) { - CTL_WRN("Model is empty"); - } + server_map_[model_id].host = "127.0.0.1"; server_map_[model_id].port = GenerateRandomInteger(39400, 39999); auto& s = server_map_[model_id]; @@ -545,6 +564,8 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, params.push_back("--pooling"); params.push_back("mean"); + params.push_back("--jinja"); + std::vector v; v.reserve(params.size() + 1); auto engine_dir = engine_service_.GetEngineDirPath(kLlamaRepo); diff --git a/engine/services/model_source_service.cc b/engine/services/model_source_service.cc index b5979667c..661b9b580 100644 --- a/engine/services/model_source_service.cc +++ b/engine/services/model_source_service.cc @@ -433,8 +433,7 @@ cpp::result ModelSourceService::AddCortexsoRepo( auto author = hub_author; auto model_author = hu::GetModelAuthorCortexsoHub(model_name); - if (auto model_author = hu::GetModelAuthorCortexsoHub(model_name); - model_author.has_value() && !model_author.value().empty()) { + if (model_author.has_value() && !model_author.value().empty()) { author = model_author.value(); } From 3a638267c26016d25b53ef936325b0b765487f1f Mon Sep 17 00:00:00 2001 From: Akarshan Biswas Date: Thu, 12 Jun 2025 13:47:26 +0530 Subject: [PATCH 43/49] Hostfix: remove not needed params from load_model (#2209) * refactor: remove --pooling flag from model loading The --pooling flag was removed as the mean pooling functionality not needed in chat models. This fixes the regression * feat(local-engine): add ctx_len parameter support Adds support for the ctx_len parameter by appending --ctx-size with its value. Removed outdated parameter mappings from the kParamsMap to reflect current implementation details and ensure consistency. * feat: add conditional model parameters based on path When the model path contains both "jan" and "nano" (case-insensitive), automatically add speculative decoding parameters to adjust generation behavior. This improves flexibility by enabling environment-specific configurations without manual parameter tuning. Also includes necessary headers for string manipulation and fixes whitespace in ctx_len handling. * chore: remove redundant comment The comment was redundant as the code's purpose is clear without it, improving readability. --- .../extensions/local-engine/local_engine.cc | 28 ++++++++++++++---- engine/services/model_service.cc | 29 ++++++++++++------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index 2bba11a7b..beda1f44b 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -1,6 +1,9 @@ #include "local_engine.h" +#include #include +#include #include +#include #include #include "utils/curl_utils.h" #include "utils/json_helper.h" @@ -20,6 +23,7 @@ const std::unordered_set kIgnoredParams = { "user_prompt", "min_keep", "mirostat", "mirostat_eta", "mirostat_tau", "text_model", "version", "n_probs", "object", "penalize_nl", "precision", "size", + "flash_attn", "stop", "tfs_z", "typ_p", "caching_enabled"}; const std::unordered_map kParamsMap = { @@ -42,18 +46,24 @@ int GenerateRandomInteger(int min, int max) { std::uniform_int_distribution<> dis( min, max); // Distribution for the desired range - return dis(gen); // Generate and return a random integer within the range + return dis(gen); } std::vector ConvertJsonToParamsVector(const Json::Value& root) { std::vector res; - std::string errors; for (const auto& member : root.getMemberNames()) { if (member == "model_path" || member == "llama_model_path") { if (!root[member].isNull()) { + const std::string path = root[member].asString(); res.push_back("--model"); - res.push_back(root[member].asString()); + res.push_back(path); + + // If path contains both "Jan" and "nano", case-insensitive, add special params + std::string lowered = path; + std::transform(lowered.begin(), lowered.end(), lowered.begin(), [](unsigned char c) { + return std::tolower(c); + }); } continue; } else if (kIgnoredParams.find(member) != kIgnoredParams.end()) { @@ -85,8 +95,15 @@ std::vector ConvertJsonToParamsVector(const Json::Value& root) { res.push_back("--ignore_eos"); } continue; + } else if (member == "ctx_len") { + if (!root[member].isNull()) { + res.push_back("--ctx-size"); + res.push_back(root[member].asString()); + } + continue; } + // Generic handling for other members res.push_back("--" + member); if (root[member].isString()) { res.push_back(root[member].asString()); @@ -105,7 +122,7 @@ std::vector ConvertJsonToParamsVector(const Json::Value& root) { ss << "\"" << value.asString() << "\""; first = false; } - ss << "] "; + ss << "]"; res.push_back(ss.str()); } } @@ -113,6 +130,7 @@ std::vector ConvertJsonToParamsVector(const Json::Value& root) { return res; } + constexpr const auto kMinDataChunkSize = 6u; struct OaiInfo { @@ -561,8 +579,6 @@ void LocalEngine::LoadModel(std::shared_ptr json_body, params.push_back("--port"); params.push_back(std::to_string(s.port)); - params.push_back("--pooling"); - params.push_back("mean"); params.push_back("--jinja"); diff --git a/engine/services/model_service.cc b/engine/services/model_service.cc index 68f0fe070..2da6c749e 100644 --- a/engine/services/model_service.cc +++ b/engine/services/model_service.cc @@ -165,8 +165,8 @@ ModelService::ModelService(std::shared_ptr db_service, download_service_{download_service}, inference_svc_(inference_service), engine_svc_(engine_svc), - task_queue_(task_queue) { - // ProcessBgrTasks(); + task_queue_(task_queue){ + // ProcessBgrTasks(); }; void ModelService::ForceIndexingModelList() { @@ -557,6 +557,8 @@ cpp::result ModelService::StartModel( if (auto& o = params_override["ctx_len"]; !o.isNull()) { ctx_len = o.asInt(); } + Json::Value model_load_params; + json_helper::MergeJson(model_load_params, params_override); try { constexpr const int kDefautlContextLength = 8192; @@ -630,6 +632,8 @@ cpp::result ModelService::StartModel( #else json_data["model_path"] = fmu::ToAbsoluteCortexDataPath(fs::path(mc.files[0])).string(); + model_load_params["model_path"] = + fmu::ToAbsoluteCortexDataPath(fs::path(mc.files[0])).string(); #endif } else { LOG_WARN << "model_path is empty"; @@ -642,6 +646,8 @@ cpp::result ModelService::StartModel( #else json_data["mmproj"] = fmu::ToAbsoluteCortexDataPath(fs::path(mc.mmproj)).string(); + model_load_params["model_path"] = + fmu::ToAbsoluteCortexDataPath(fs::path(mc.mmproj)).string(); #endif } json_data["system_prompt"] = mc.system_template; @@ -655,6 +661,7 @@ cpp::result ModelService::StartModel( } json_data["model"] = model_handle; + model_load_params["model"] = model_handle; if (auto& cpt = custom_prompt_template; !cpt.value_or("").empty()) { auto parse_prompt_result = string_utils::ParsePrompt(cpt.value()); json_data["system_prompt"] = parse_prompt_result.system_prompt; @@ -662,8 +669,6 @@ cpp::result ModelService::StartModel( json_data["ai_prompt"] = parse_prompt_result.ai_prompt; } - json_helper::MergeJson(json_data, params_override); - // Set default cpu_threads if it is not configured if (!json_data.isMember("cpu_threads")) { json_data["cpu_threads"] = GetCpuThreads(); @@ -686,12 +691,12 @@ cpp::result ModelService::StartModel( assert(!!inference_svc_); - auto ir = - inference_svc_->LoadModel(std::make_shared(json_data)); + auto ir = inference_svc_->LoadModel( + std::make_shared(model_load_params)); auto status = std::get<0>(ir)["status_code"].asInt(); auto data = std::get<1>(ir); - if (status == drogon::k200OK) { + if (status == drogon::k200OK) { return StartModelResult{/* .success = */ true, /* .warning = */ may_fallback_res.value()}; } else if (status == drogon::k409Conflict) { @@ -1031,13 +1036,15 @@ ModelService::MayFallbackToCpu(const std::string& model_path, int ngl, auto es = hardware::EstimateLLaMACppRun(model_path, rc); if (!!es && (*es).gpu_mode.vram_MiB > free_vram_MiB && is_cuda) { - CTL_WRN("Not enough VRAM - " << "required: " << (*es).gpu_mode.vram_MiB - << ", available: " << free_vram_MiB); + CTL_WRN("Not enough VRAM - " + << "required: " << (*es).gpu_mode.vram_MiB + << ", available: " << free_vram_MiB); } if (!!es && (*es).cpu_mode.ram_MiB > free_ram_MiB) { - CTL_WRN("Not enough RAM - " << "required: " << (*es).cpu_mode.ram_MiB - << ", available: " << free_ram_MiB); + CTL_WRN("Not enough RAM - " + << "required: " << (*es).cpu_mode.ram_MiB + << ", available: " << free_ram_MiB); } return warning; From 9e87efca30e017c052c74e574bb998c9b0692a7a Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 12 Jun 2025 19:51:52 +0700 Subject: [PATCH 44/49] fix: do not ignore client request param (#2210) --- engine/extensions/local-engine/local_engine.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index beda1f44b..adc8649f6 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -23,7 +23,6 @@ const std::unordered_set kIgnoredParams = { "user_prompt", "min_keep", "mirostat", "mirostat_eta", "mirostat_tau", "text_model", "version", "n_probs", "object", "penalize_nl", "precision", "size", - "flash_attn", "stop", "tfs_z", "typ_p", "caching_enabled"}; const std::unordered_map kParamsMap = { From aab35862051ec45239e69060e4c150374a7d2bf9 Mon Sep 17 00:00:00 2001 From: Akarshan Biswas Date: Sat, 14 Jun 2025 11:03:14 +0530 Subject: [PATCH 45/49] feat: add reasoning_budget parameter to params map (#2211) --- engine/extensions/local-engine/local_engine.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/extensions/local-engine/local_engine.cc b/engine/extensions/local-engine/local_engine.cc index adc8649f6..74bf0d1b8 100644 --- a/engine/extensions/local-engine/local_engine.cc +++ b/engine/extensions/local-engine/local_engine.cc @@ -37,6 +37,7 @@ const std::unordered_map kParamsMap = { {"dynatemp_exponent", "--dynatemp-exp"}, {"ctx_len", "--ctx-size"}, {"ngl", "-ngl"}, + {"reasoning_budget", "--reasoning-budget"}, }; int GenerateRandomInteger(int min, int max) { @@ -50,6 +51,8 @@ int GenerateRandomInteger(int min, int max) { std::vector ConvertJsonToParamsVector(const Json::Value& root) { std::vector res; + std::string errors; + res.push_back("--no-webui"); for (const auto& member : root.getMemberNames()) { if (member == "model_path" || member == "llama_model_path") { From 62b74b7af9f94e23d4ffd10d8a185d0efb8abb12 Mon Sep 17 00:00:00 2001 From: ethanova Date: Sat, 14 Jun 2025 02:29:32 -0400 Subject: [PATCH 46/49] fix bug where for local models, delete only the model passed in, not all local models (#2207) Co-authored-by: Ethan Garber Co-authored-by: Akarshan Biswas --- engine/services/model_service.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/engine/services/model_service.cc b/engine/services/model_service.cc index 2da6c749e..51e42ff81 100644 --- a/engine/services/model_service.cc +++ b/engine/services/model_service.cc @@ -500,13 +500,10 @@ cpp::result ModelService::DeleteModel( std::filesystem::remove(yaml_fp); CTL_INF("Removed: " << yaml_fp.string()); } else { - // Remove yaml files - for (const auto& entry : - std::filesystem::directory_iterator(yaml_fp.parent_path())) { - if (entry.is_regular_file() && (entry.path().extension() == ".yml")) { - std::filesystem::remove(entry); - CTL_INF("Removed: " << entry.path().string()); - } + // Is a local model - Remove only this model's yaml file + if (std::filesystem::exists(yaml_fp)) { + std::filesystem::remove(yaml_fp); + CTL_INF("Removed: " << yaml_fp.string()); } } From 4cc2166204ef2c3bddb187c9ea3055caca615c9e Mon Sep 17 00:00:00 2001 From: Louis Date: Sun, 15 Jun 2025 16:44:09 +0700 Subject: [PATCH 47/49] fix: model lookup issue on Windows (#2213) --- engine/services/model_service.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/services/model_service.cc b/engine/services/model_service.cc index 51e42ff81..a3771e0a1 100644 --- a/engine/services/model_service.cc +++ b/engine/services/model_service.cc @@ -626,6 +626,9 @@ cpp::result ModelService::StartModel( #if defined(_WIN32) json_data["model_path"] = cortex::wc::WstringToUtf8( fmu::ToAbsoluteCortexDataPath(fs::path(mc.files[0])).wstring()); + model_load_params["model_path"] = + cortex::wc::WstringToUtf8( + fmu::ToAbsoluteCortexDataPath(fs::path(mc.files[0])).wstring()); #else json_data["model_path"] = fmu::ToAbsoluteCortexDataPath(fs::path(mc.files[0])).string(); From cc390f26400ce80732fa81fc6f20ae23a1f88795 Mon Sep 17 00:00:00 2001 From: Service Account Date: Fri, 4 Jul 2025 14:33:36 +0700 Subject: [PATCH 48/49] chore: update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 5cd51ece1..ad4a379de 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +> ⚠️ **Archived Notice** +> This repository is no longer actively maintained. +> We have migrated development to [menloresearch/llama.cpp](https://github.com/menloresearch/llama.cpp). +> Please contribute directly to `llama.cpp` moving forward. + # Cortex