From 9ba488620d188c8a732c7e38dd19279b512ee9ad Mon Sep 17 00:00:00 2001 From: NamH Date: Fri, 27 Dec 2024 08:27:44 +0700 Subject: [PATCH 1/3] fix: not create new folder if is registering paths (#1828) --- engine/services/engine_service.cc | 7 ++++--- engine/utils/file_manager_utils.cc | 5 +++-- engine/utils/file_manager_utils.h | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc index c8f4c180c..79b50df36 100644 --- a/engine/services/engine_service.cc +++ b/engine/services/engine_service.cc @@ -132,8 +132,8 @@ cpp::result EngineService::UnzipEngine( CTL_INF("Found cuda variant, extract it"); found_cuda = true; // extract binary - auto cuda_path = - file_manager_utils::GetCudaToolkitPath(NormalizeEngine(engine)); + auto cuda_path = file_manager_utils::GetCudaToolkitPath( + NormalizeEngine(engine), true); archive_utils::ExtractArchive(path + "/" + cf, cuda_path.string(), true); } @@ -434,7 +434,8 @@ cpp::result EngineService::DownloadCuda( }}; auto on_finished = [engine](const DownloadTask& finishedTask) { - auto engine_path = file_manager_utils::GetCudaToolkitPath(engine); + auto engine_path = file_manager_utils::GetCudaToolkitPath(engine, true); + archive_utils::ExtractArchive(finishedTask.items[0].localPath.string(), engine_path.string()); try { diff --git a/engine/utils/file_manager_utils.cc b/engine/utils/file_manager_utils.cc index 338abadac..aee65020c 100644 --- a/engine/utils/file_manager_utils.cc +++ b/engine/utils/file_manager_utils.cc @@ -289,13 +289,14 @@ std::filesystem::path GetModelsContainerPath() { return models_container_path; } -std::filesystem::path GetCudaToolkitPath(const std::string& engine) { +std::filesystem::path GetCudaToolkitPath(const std::string& engine, + bool create_if_not_exist) { auto engine_path = getenv("ENGINE_PATH") ? std::filesystem::path(getenv("ENGINE_PATH")) : GetCortexDataPath(); auto cuda_path = engine_path / "engines" / engine / "deps"; - if (!std::filesystem::exists(cuda_path)) { + if (create_if_not_exist && !std::filesystem::exists(cuda_path)) { std::filesystem::create_directories(cuda_path); } diff --git a/engine/utils/file_manager_utils.h b/engine/utils/file_manager_utils.h index 91102d002..059fe6ae3 100644 --- a/engine/utils/file_manager_utils.h +++ b/engine/utils/file_manager_utils.h @@ -45,7 +45,8 @@ void CreateDirectoryRecursively(const std::string& path); std::filesystem::path GetModelsContainerPath(); -std::filesystem::path GetCudaToolkitPath(const std::string& engine); +std::filesystem::path GetCudaToolkitPath(const std::string& engine, + bool create_if_not_exist = false); std::filesystem::path GetEnginesContainerPath(); From 3cc1e71520c6482a2076d1a601d2f489e53d788d Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Thu, 26 Dec 2024 09:25:29 +0700 Subject: [PATCH 2/3] fix: forward start model parameters (#1825) Co-authored-by: vansangpfiev --- engine/controllers/models.cc | 53 ++++++-------------- engine/services/model_service.cc | 35 +++++++------ engine/services/model_service.h | 19 +------ engine/test/components/test_json_helper.cc | 58 ++++++++++++++++++++++ engine/utils/json_helper.h | 24 +++++++++ 5 files changed, 116 insertions(+), 73 deletions(-) diff --git a/engine/controllers/models.cc b/engine/controllers/models.cc index 59793b2a6..1c33ab1dc 100644 --- a/engine/controllers/models.cc +++ b/engine/controllers/models.cc @@ -488,55 +488,31 @@ void Models::StartModel( if (!http_util::HasFieldInReq(req, callback, "model")) return; auto model_handle = (*(req->getJsonObject())).get("model", "").asString(); - StartParameterOverride params_override; - if (auto& o = (*(req->getJsonObject()))["prompt_template"]; !o.isNull()) { - params_override.custom_prompt_template = o.asString(); - } - - if (auto& o = (*(req->getJsonObject()))["cache_enabled"]; !o.isNull()) { - params_override.cache_enabled = o.asBool(); - } - - if (auto& o = (*(req->getJsonObject()))["ngl"]; !o.isNull()) { - params_override.ngl = o.asInt(); - } - - if (auto& o = (*(req->getJsonObject()))["n_parallel"]; !o.isNull()) { - params_override.n_parallel = o.asInt(); - } - - if (auto& o = (*(req->getJsonObject()))["ctx_len"]; !o.isNull()) { - params_override.ctx_len = o.asInt(); - } - - if (auto& o = (*(req->getJsonObject()))["cache_type"]; !o.isNull()) { - params_override.cache_type = o.asString(); - } + std::optional mmproj; if (auto& o = (*(req->getJsonObject()))["mmproj"]; !o.isNull()) { - params_override.mmproj = o.asString(); + mmproj = o.asString(); } + auto bypass_llama_model_path = false; // Support both llama_model_path and model_path for backward compatible // model_path has higher priority if (auto& o = (*(req->getJsonObject()))["llama_model_path"]; !o.isNull()) { - params_override.model_path = o.asString(); + auto model_path = o.asString(); if (auto& mp = (*(req->getJsonObject()))["model_path"]; mp.isNull()) { // Bypass if model does not exist in DB and llama_model_path exists - if (std::filesystem::exists(params_override.model_path.value()) && + if (std::filesystem::exists(model_path) && !model_service_->HasModel(model_handle)) { CTL_INF("llama_model_path exists, bypass check model id"); - params_override.bypass_llama_model_path = true; + bypass_llama_model_path = true; } } } - if (auto& o = (*(req->getJsonObject()))["model_path"]; !o.isNull()) { - params_override.model_path = o.asString(); - } + auto bypass_model_check = (mmproj.has_value() || bypass_llama_model_path); auto model_entry = model_service_->GetDownloadedModel(model_handle); - if (!model_entry.has_value() && !params_override.bypass_model_check()) { + if (!model_entry.has_value() && !bypass_model_check) { Json::Value ret; ret["message"] = "Cannot find model: " + model_handle; auto resp = cortex_utils::CreateCortexHttpJsonResponse(ret); @@ -544,9 +520,8 @@ void Models::StartModel( callback(resp); return; } - std::string engine_name = params_override.bypass_model_check() - ? kLlamaEngine - : model_entry.value().engine; + std::string engine_name = + bypass_model_check ? kLlamaEngine : model_entry.value().engine; auto engine_validate = engine_service_->IsEngineReady(engine_name); if (engine_validate.has_error()) { Json::Value ret; @@ -565,7 +540,9 @@ void Models::StartModel( return; } - auto result = model_service_->StartModel(model_handle, params_override); + auto result = model_service_->StartModel( + model_handle, *(req->getJsonObject()) /*params_override*/, + bypass_model_check); if (result.has_error()) { Json::Value ret; ret["message"] = result.error(); @@ -668,7 +645,7 @@ void Models::AddRemoteModel( auto model_handle = (*(req->getJsonObject())).get("model", "").asString(); auto engine_name = (*(req->getJsonObject())).get("engine", "").asString(); - + auto engine_validate = engine_service_->IsEngineReady(engine_name); if (engine_validate.has_error()) { Json::Value ret; @@ -687,7 +664,7 @@ void Models::AddRemoteModel( callback(resp); return; } - + config::RemoteModelConfig model_config; model_config.LoadFromJson(*(req->getJsonObject())); cortex::db::Models modellist_utils_obj; diff --git a/engine/services/model_service.cc b/engine/services/model_service.cc index ce83152c4..b79894ca1 100644 --- a/engine/services/model_service.cc +++ b/engine/services/model_service.cc @@ -748,19 +748,28 @@ cpp::result ModelService::DeleteModel( } cpp::result ModelService::StartModel( - const std::string& model_handle, - const StartParameterOverride& params_override) { + const std::string& model_handle, const Json::Value& params_override, + bool bypass_model_check) { namespace fs = std::filesystem; namespace fmu = file_manager_utils; cortex::db::Models modellist_handler; config::YamlHandler yaml_handler; + std::optional custom_prompt_template; + std::optional ctx_len; + if (auto& o = params_override["prompt_template"]; !o.isNull()) { + custom_prompt_template = o.asString(); + } + + if (auto& o = params_override["ctx_len"]; !o.isNull()) { + ctx_len = o.asInt(); + } try { constexpr const int kDefautlContextLength = 8192; int max_model_context_length = kDefautlContextLength; Json::Value json_data; // Currently we don't support download vision models, so we need to bypass check - if (!params_override.bypass_model_check()) { + if (!bypass_model_check) { auto model_entry = modellist_handler.GetModelInfo(model_handle); if (model_entry.has_error()) { CTL_WRN("Error: " + model_entry.error()); @@ -838,29 +847,19 @@ cpp::result ModelService::StartModel( } json_data["model"] = model_handle; - if (auto& cpt = params_override.custom_prompt_template; - !cpt.value_or("").empty()) { + 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; json_data["user_prompt"] = parse_prompt_result.user_prompt; json_data["ai_prompt"] = parse_prompt_result.ai_prompt; } -#define ASSIGN_IF_PRESENT(json_obj, param_override, param_name) \ - if (param_override.param_name) { \ - json_obj[#param_name] = param_override.param_name.value(); \ - } + json_helper::MergeJson(json_data, params_override); - ASSIGN_IF_PRESENT(json_data, params_override, cache_enabled); - ASSIGN_IF_PRESENT(json_data, params_override, ngl); - ASSIGN_IF_PRESENT(json_data, params_override, n_parallel); - ASSIGN_IF_PRESENT(json_data, params_override, cache_type); - ASSIGN_IF_PRESENT(json_data, params_override, mmproj); - ASSIGN_IF_PRESENT(json_data, params_override, model_path); -#undef ASSIGN_IF_PRESENT - if (params_override.ctx_len) { + // Set the latest ctx_len + if (ctx_len) { json_data["ctx_len"] = - std::min(params_override.ctx_len.value(), max_model_context_length); + std::min(ctx_len.value(), max_model_context_length); } CTL_INF(json_data.toStyledString()); auto may_fallback_res = MayFallbackToCpu(json_data["model_path"].asString(), diff --git a/engine/services/model_service.h b/engine/services/model_service.h index e2638fd1f..35c5ba539 100644 --- a/engine/services/model_service.h +++ b/engine/services/model_service.h @@ -18,21 +18,6 @@ struct ModelPullInfo { std::string download_url; }; -struct StartParameterOverride { - std::optional cache_enabled; - std::optional ngl; - std::optional n_parallel; - std::optional ctx_len; - std::optional custom_prompt_template; - std::optional cache_type; - std::optional mmproj; - std::optional model_path; - bool bypass_llama_model_path = false; - bool bypass_model_check() const { - return mmproj.has_value() || bypass_llama_model_path; - } -}; - struct StartModelResult { bool success; std::optional warning; @@ -78,8 +63,8 @@ class ModelService { cpp::result DeleteModel(const std::string& model_handle); cpp::result StartModel( - const std::string& model_handle, - const StartParameterOverride& params_override); + const std::string& model_handle, const Json::Value& params_override, + bool bypass_model_check); cpp::result StopModel(const std::string& model_handle); diff --git a/engine/test/components/test_json_helper.cc b/engine/test/components/test_json_helper.cc index cb3f4683a..ba5e27165 100644 --- a/engine/test/components/test_json_helper.cc +++ b/engine/test/components/test_json_helper.cc @@ -33,3 +33,61 @@ TEST(ParseJsonStringTest, EmptyString) { EXPECT_TRUE(result.isNull()); } + +TEST(MergeJsonTest, MergeSimpleObjects) { + Json::Value json1, json2; + json1["name"] = "John"; + json1["age"] = 30; + + json2["age"] = 31; + json2["email"] = "john@example.com"; + + json_helper::MergeJson(json1, json2); + + Json::Value expected; + expected["name"] = "John"; + expected["age"] = 31; + expected["email"] = "john@example.com"; + + EXPECT_EQ(json1, expected); +} + +TEST(MergeJsonTest, MergeNestedObjects) { + Json::Value json1, json2; + json1["person"]["name"] = "John"; + json1["person"]["age"] = 30; + + json2["person"]["age"] = 31; + json2["person"]["email"] = "john@example.com"; + + json_helper::MergeJson(json1, json2); + + Json::Value expected; + expected["person"]["name"] = "John"; + expected["person"]["age"] = 31; + expected["person"]["email"] = "john@example.com"; + + EXPECT_EQ(json1, expected); +} + +TEST(MergeJsonTest, MergeArrays) { + Json::Value json1, json2; + json1["hobbies"] = Json::Value(Json::arrayValue); + json1["hobbies"].append("reading"); + json1["hobbies"].append("painting"); + + json2["hobbies"] = Json::Value(Json::arrayValue); + json2["hobbies"].append("hiking"); + json2["hobbies"].append("painting"); + + json_helper::MergeJson(json1, json2); + + Json::Value expected; + expected["hobbies"] = Json::Value(Json::arrayValue); + expected["hobbies"].append("reading"); + expected["hobbies"].append("painting"); + expected["hobbies"].append("hiking"); + expected["hobbies"].append("painting"); + + EXPECT_EQ(json1, expected); +} diff --git a/engine/utils/json_helper.h b/engine/utils/json_helper.h index 82f994751..3b08651c4 100644 --- a/engine/utils/json_helper.h +++ b/engine/utils/json_helper.h @@ -16,4 +16,28 @@ inline std::string DumpJsonString(const Json::Value& json) { builder["indentation"] = ""; return Json::writeString(builder, json); } + +inline void MergeJson(Json::Value& target, const Json::Value& source) { + for (const auto& member : source.getMemberNames()) { + if (target.isMember(member)) { + // If the member exists in both objects, recursively merge the values + if (target[member].type() == Json::objectValue && + source[member].type() == Json::objectValue) { + MergeJson(target[member], source[member]); + } else if (target[member].type() == Json::arrayValue && + source[member].type() == Json::arrayValue) { + // If the member is an array in both objects, merge the arrays + for (const auto& value : source[member]) { + target[member].append(value); + } + } else { + // Otherwise, overwrite the value in the target with the value from the source + target[member] = source[member]; + } + } else { + // If the member doesn't exist in the target, add it + target[member] = source[member]; + } + } +} } // namespace json_helper From 5825412b362198c3dd7083b7dc33717cc596dbba Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Wed, 8 Jan 2025 09:19:08 +0700 Subject: [PATCH 3/3] fix: build linux arm (#1806) * fix: build linux arm * feat: cicd arm64 * fix: build linux arm * feat: cicd arm64 * fix: e2e test linux arm64 * fix: select linux arm64 * chore: e2e tests * fix: ci linux arm * fix: ci correct artifact name * fix: linux installer arm64 --------- Co-authored-by: Hien To Co-authored-by: sangjanai Co-authored-by: Service Account --- .github/workflows/beta-build.yml | 26 +++++- .github/workflows/cortex-cpp-quality-gate.yml | 36 +++++++++ .github/workflows/nightly-build.yml | 21 ++++- .github/workflows/stable-build.yml | 18 ++++- ...linux-x64.yml => template-build-linux.yml} | 80 ++++++++++++------- engine/Makefile | 3 +- engine/e2e-test/test_api_engine.py | 4 +- engine/e2e-test/test_api_engine_update.py | 2 +- engine/e2e-test/test_api_model.py | 1 + engine/e2e-test/test_cli_engine_install.py | 4 +- .../test_cli_engine_install_nightly.py | 1 + engine/e2e-test/test_cli_engine_uninstall.py | 2 +- engine/services/download_service.cc | 10 +-- engine/templates/linux/control | 2 +- engine/templates/linux/create_deb.sh | 4 +- engine/templates/linux/create_deb_local.sh | 3 +- engine/templates/linux/install.sh | 29 ++++--- .../components/test_engine_matcher_utils.cc | 13 +++ engine/utils/curl_utils.cc | 77 +++++++++++------- engine/utils/curl_utils.h | 7 +- engine/utils/engine_matcher_utils.h | 5 ++ 21 files changed, 255 insertions(+), 93 deletions(-) rename .github/workflows/{template-build-linux-x64.yml => template-build-linux.yml} (66%) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index c5c09dcb5..bdc277231 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -67,7 +67,7 @@ jobs: cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} build-linux-x64: - uses: ./.github/workflows/template-build-linux-x64.yml + uses: ./.github/workflows/template-build-linux.yml secrets: inherit needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] with: @@ -79,6 +79,22 @@ jobs: 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 }} + 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] + 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 }} + cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + arch: arm64 build-docker-x64: uses: ./.github/workflows/template-build-docker-x64.yml @@ -111,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, update_release] + needs: [get-update-version, create-draft-release, build-macos, build-windows-x64, build-linux-x64, build-linux-arm64, update_release] runs-on: ubuntu-latest permissions: contents: write @@ -136,9 +152,13 @@ jobs: - Network Installer: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-mac-universal-network-installer.pkg - Local Installer: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-mac-universal-local-installer.pkg - Binary: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-mac-universal.tar.gz - - Linux Deb: + - Linux amd64 Deb: - Network Installer: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-linux-amd64-network-installer.deb - Local Installer: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-linux-amd64-local-installer.deb - Binary: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-linux-amd64.tar.gz + - Linux amd64 Deb: + - Network Installer: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-linux-arm64-network-installer.deb + - Local Installer: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-linux-arm64-local-installer.deb + - Binary: https://github.com/janhq/cortex.cpp/releases/download/v${{ env.VERSION }}/cortex-${{ env.VERSION }}-linux-arm64.tar.gz - Docker: menloltd/cortex:beta-${{ env.VERSION }} - Github Release: https://github.com/janhq/cortex.cpp/releases/tag/v${{ env.VERSION }} \ No newline at end of file diff --git a/.github/workflows/cortex-cpp-quality-gate.yml b/.github/workflows/cortex-cpp-quality-gate.yml index 8a76e4669..fd98930a1 100644 --- a/.github/workflows/cortex-cpp-quality-gate.yml +++ b/.github/workflows/cortex-cpp-quality-gate.yml @@ -20,6 +20,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: "amd64" runs-on: "ubuntu-20-04-cuda-12-0" @@ -52,6 +58,7 @@ jobs: submodules: recursive - name: use python + continue-on-error: true uses: actions/setup-python@v5 with: python-version: "3.10" @@ -90,15 +97,44 @@ jobs: AWS_DEFAULT_REGION: "${{ secrets.MINIO_REGION }}" - name: Configure vcpkg + if: runner.os != 'Linux' + run: | + cd engine + make configure-vcpkg + + - name: Configure vcpkg linux amd64 + if: runner.os != 'Linux' + run: | + cd engine + make configure-vcpkg + + - name: Configure vcpkg linux arm64 + if: runner.os == 'Linux' run: | cd engine + # Set env if arch is arm64 + if [ "${{ matrix.name }}" == "arm64" ]; then + sudo apt install ninja-build pkg-config -y + export VCPKG_FORCE_SYSTEM_BINARIES=1 + fi make configure-vcpkg - name: Build + if: runner.os != 'Linux' run: | cd engine make build CMAKE_EXTRA_FLAGS="${{ matrix.cmake-flags }}" BUILD_DEPS_CMAKE_EXTRA_FLAGS="${{ matrix.build-deps-cmake-flags }}" + - name: Build + if: runner.os == 'Linux' + run: | + cd engine + if [ "${{ matrix.name }}" == "arm64" ]; then + export VCPKG_FORCE_SYSTEM_BINARIES=1 + fi + make build CMAKE_EXTRA_FLAGS="${{ matrix.cmake-flags }}" BUILD_DEPS_CMAKE_EXTRA_FLAGS="${{ matrix.build-deps-cmake-flags }}" + + - name: Run setup config run: | cd engine diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 9a31ef5ff..1f076dc97 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -74,7 +74,7 @@ jobs: cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} build-linux-x64: - uses: ./.github/workflows/template-build-linux-x64.yml + uses: ./.github/workflows/template-build-linux.yml secrets: inherit needs: [get-update-version, set-public-provider, get-cortex-llamacpp-latest-version] with: @@ -85,11 +85,26 @@ jobs: 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 }} + 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] + 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 + cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_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-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-cortex-llamacpp-latest-version] steps: - name: Update latest version id: update-latest-version @@ -100,9 +115,11 @@ jobs: aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/mac-universal-cortex-nightly.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/mac-arm64/cortex-nightly.tar.gz aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/mac-universal-cortex-nightly.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/mac-universal/cortex-nightly.tar.gz aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/linux-amd64-cortex-nightly.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/linux-amd64/cortex-nightly.tar.gz + aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/linux-arm64-cortex-nightly.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/linux-arm64/cortex-nightly.tar.gz aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/windows-amd64-cortex-nightly.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/windows-amd64/cortex-nightly.tar.gz aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/cortex-mac-universal-network-installer.pkg s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/mac-universal/cortex-mac-universal-network-installer.pkg aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/cortex-linux-amd64-network-installer.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/linux-amd64/cortex-linux-amd64-network-installer.deb + aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/cortex-linux-arm64-network-installer.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/linux-arm64/cortex-linux-arm64-network-installer.deb aws s3 cp s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/cortex-windows-amd64-network-installer.exe s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/latest/windows-amd64/cortex-windows-amd64-network-installer.exe env: diff --git a/.github/workflows/stable-build.yml b/.github/workflows/stable-build.yml index 2b0523771..b05df983d 100644 --- a/.github/workflows/stable-build.yml +++ b/.github/workflows/stable-build.yml @@ -67,7 +67,7 @@ jobs: cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} build-linux-x64: - uses: ./.github/workflows/template-build-linux-x64.yml + uses: ./.github/workflows/template-build-linux.yml secrets: inherit needs: [get-update-version, create-draft-release, get-cortex-llamacpp-latest-version] with: @@ -79,6 +79,22 @@ jobs: 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 }} + 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] + 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 }} + cortex-llamacpp-version: ${{ needs.get-cortex-llamacpp-latest-version.outputs.cortex_llamacpp_latest_version }} + arch: arm64 build-docker-x64: uses: ./.github/workflows/template-build-docker-x64.yml diff --git a/.github/workflows/template-build-linux-x64.yml b/.github/workflows/template-build-linux.yml similarity index 66% rename from .github/workflows/template-build-linux-x64.yml rename to .github/workflows/template-build-linux.yml index d1ca73844..02cc3a187 100644 --- a/.github/workflows/template-build-linux-x64.yml +++ b/.github/workflows/template-build-linux.yml @@ -1,4 +1,4 @@ -name: build-linux-x64 +name: build-linux on: workflow_call: inputs: @@ -49,6 +49,11 @@ on: type: string default: '0.0.0' description: 'The version of cortex-llamacpp to use for this job' + arch: + required: false + type: string + default: 'amd64' + description: 'The architecture to use for this job' secrets: DELTA_AWS_S3_BUCKET_NAME: required: false @@ -60,7 +65,7 @@ on: required: false jobs: - build-linux-x64: + build-linux: runs-on: ${{ inputs.runs-on }} permissions: contents: write @@ -72,6 +77,7 @@ jobs: submodules: 'recursive' - name: use python 3.9 + continue-on-error: true uses: actions/setup-python@v4 with: python-version: '3.9' @@ -124,14 +130,24 @@ jobs: - name: Configure vcpkg run: | cd engine + # Set env if arch is arm64 + if [ "${{ inputs.arch }}" == "arm64" ]; then + sudo apt install ninja-build pkg-config -y + export VCPKG_FORCE_SYSTEM_BINARIES=1 + fi make configure-vcpkg - name: Build run: | cd engine + # Set env if arch is arm64 + if [ "${{ inputs.arch }}" == "arm64" ]; then + export VCPKG_FORCE_SYSTEM_BINARIES=1 + fi make build CMAKE_EXTRA_FLAGS="${{ inputs.cmake-flags }}" BUILD_DEPS_CMAKE_EXTRA_FLAGS="${{ inputs.build-deps-cmake-flags }}" - name: Install Python + continue-on-error: true uses: actions/setup-python@v4 with: python-version: '3.10' @@ -145,28 +161,32 @@ jobs: shell: bash run: | cd engine - make build-installer PACKAGE_NAME="${{ steps.set-output-params.outputs.package_name }}" SOURCE_BINARY_PATH="../../cortex/${{ steps.set-output-params.outputs.destination_binary_name }}" SOURCE_BINARY_SERVER_PATH="../../cortex/${{ steps.set-output-params.outputs.destination_binary_server_name }}" VERSION=${{ inputs.new_version }} DESTINATION_BINARY_NAME="${{ steps.set-output-params.outputs.destination_binary_name }}" DESTINATION_BINARY_SERVER_NAME="${{ steps.set-output-params.outputs.destination_binary_server_name }}" DATA_FOLDER_NAME="${{ steps.set-output-params.outputs.data_folder_name }}" CONFIGURATION_FILE_NAME="${{ steps.set-output-params.outputs.configuration_file_name }}" UNINSTALLER_FILE_NAME="${{ steps.set-output-params.outputs.uninstaller_file_name }}" + make build-installer PACKAGE_NAME="${{ steps.set-output-params.outputs.package_name }}" SOURCE_BINARY_PATH="../../cortex/${{ steps.set-output-params.outputs.destination_binary_name }}" SOURCE_BINARY_SERVER_PATH="../../cortex/${{ steps.set-output-params.outputs.destination_binary_server_name }}" VERSION=${{ inputs.new_version }} DESTINATION_BINARY_NAME="${{ steps.set-output-params.outputs.destination_binary_name }}" DESTINATION_BINARY_SERVER_NAME="${{ steps.set-output-params.outputs.destination_binary_server_name }}" DATA_FOLDER_NAME="${{ steps.set-output-params.outputs.data_folder_name }}" CONFIGURATION_FILE_NAME="${{ steps.set-output-params.outputs.configuration_file_name }}" UNINSTALLER_FILE_NAME="${{ steps.set-output-params.outputs.uninstaller_file_name }}" ARCH="${{ inputs.arch }}" mv ${{ steps.set-output-params.outputs.package_name }}.deb ${{ steps.set-output-params.outputs.package_name }}-network.deb - name: Build local Installers run: | mkdir -p engine/templates/linux/dependencies cd engine/templates/linux/dependencies - wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx.tar.gz - wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx2.tar.gz - wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx512.tar.gz - wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-noavx.tar.gz - wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-vulkan.tar.gz - wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-11-7-linux-amd64.tar.gz - wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-12-0-linux-amd64.tar.gz + if [ "${{ inputs.arch }}" == "amd64" ]; then + wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx.tar.gz + wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx2.tar.gz + wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-avx512.tar.gz + wget https://github.com/janhq/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/janhq/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/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-noavx.tar.gz + wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-amd64-vulkan.tar.gz + wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-11-7-linux-amd64.tar.gz + wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cuda-12-0-linux-amd64.tar.gz + else + wget https://github.com/janhq/cortex.llamacpp/releases/download/v${{ inputs.cortex-llamacpp-version }}/cortex.llamacpp-${{ inputs.cortex-llamacpp-version }}-linux-arm64.tar.gz + fi cd .. # Remove network package @@ -174,7 +194,7 @@ jobs: rm -rf ${{ steps.set-output-params.outputs.package_name }} rm ${{ steps.set-output-params.outputs.package_name }}.deb chmod +x create_deb_local.sh - ./create_deb_local.sh ${{ steps.set-output-params.outputs.package_name }} ${{ inputs.new_version }} ../../cortex/${{ steps.set-output-params.outputs.destination_binary_name }} ../../cortex/${{ steps.set-output-params.outputs.destination_binary_server_name }} ${{ steps.set-output-params.outputs.destination_binary_name }} ${{ steps.set-output-params.outputs.destination_binary_server_name }} ${{ steps.set-output-params.outputs.data_folder_name }} ${{ steps.set-output-params.outputs.configuration_file_name }}; + ./create_deb_local.sh ${{ steps.set-output-params.outputs.package_name }} ${{ inputs.new_version }} ../../cortex/${{ steps.set-output-params.outputs.destination_binary_name }} ../../cortex/${{ steps.set-output-params.outputs.destination_binary_server_name }} ${{ steps.set-output-params.outputs.destination_binary_name }} ${{ steps.set-output-params.outputs.destination_binary_server_name }} ${{ steps.set-output-params.outputs.data_folder_name }} ${{ steps.set-output-params.outputs.configuration_file_name }} ${{ inputs.arch }}; cp ${{ steps.set-output-params.outputs.package_name }}.deb ../../${{ steps.set-output-params.outputs.package_name }}-local.deb - name: Package @@ -185,30 +205,30 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: cortex-${{ inputs.new_version }}-linux-amd64 + name: cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }} path: ./engine/cortex - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: cortex-${{ inputs.new_version }}-linux-amd64-network-installer + name: cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }}-network-installer path: ./engine/${{ steps.set-output-params.outputs.package_name }}-network.deb - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: cortex-${{ inputs.new_version }}-linux-amd64-local-installer + name: cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }}-local-installer path: ./engine/${{ steps.set-output-params.outputs.package_name }}-local.deb - name: upload to aws s3 if public provider is aws if: inputs.public_provider == 'aws-s3' run: | - aws s3 cp ./engine/cortex.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/linux-amd64-cortex-nightly.tar.gz - aws s3 cp ./engine/${{ steps.set-output-params.outputs.package_name }}-network.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/cortex-linux-amd64-network-installer.deb + aws s3 cp ./engine/cortex.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/linux-${{ inputs.arch }}-cortex-nightly.tar.gz + aws s3 cp ./engine/${{ steps.set-output-params.outputs.package_name }}-network.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/temp-latest/cortex-linux-${{ inputs.arch }}-network-installer.deb - aws s3 cp ./engine/cortex.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/v${{ inputs.new_version }}/linux-amd64/cortex-nightly.tar.gz - aws s3 cp ./engine/${{ steps.set-output-params.outputs.package_name }}-network.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/v${{ inputs.new_version }}/linux-amd64/cortex-${{ inputs.new_version }}-linux-amd64-network-installer.deb - aws s3 cp ./engine/${{ steps.set-output-params.outputs.package_name }}-local.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/v${{ inputs.new_version }}/linux-amd64/cortex-${{ inputs.new_version }}-linux-amd64-local-installer.deb + aws s3 cp ./engine/cortex.tar.gz s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/v${{ inputs.new_version }}/linux-${{ inputs.arch }}/cortex-nightly.tar.gz + aws s3 cp ./engine/${{ steps.set-output-params.outputs.package_name }}-network.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/v${{ inputs.new_version }}/linux-${{ inputs.arch }}/cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }}-network-installer.deb + aws s3 cp ./engine/${{ steps.set-output-params.outputs.package_name }}-local.deb s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/cortex/v${{ inputs.new_version }}/linux-${{ inputs.arch }}/cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }}-local-installer.deb env: AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }} @@ -223,7 +243,7 @@ jobs: with: upload_url: ${{ inputs.upload_url }} asset_path: ./engine/cortex.tar.gz - asset_name: cortex-${{ inputs.new_version }}-linux-amd64.tar.gz + asset_name: cortex-${{ inputs.new_version }}-linux${{ inputs.arch }}.tar.gz asset_content_type: application/zip - name: Upload release assert if public provider is github @@ -234,7 +254,7 @@ jobs: with: upload_url: ${{ inputs.upload_url }} asset_path: ./engine/${{ steps.set-output-params.outputs.package_name }}-network.deb - asset_name: cortex-${{ inputs.new_version }}-linux-amd64-network-installer.deb + asset_name: cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }}-network-installer.deb asset_content_type: application/octet-stream - name: Upload release assert if public provider is github @@ -245,5 +265,5 @@ jobs: with: upload_url: ${{ inputs.upload_url }} asset_path: ./engine/${{ steps.set-output-params.outputs.package_name }}-local.deb - asset_name: cortex-${{ inputs.new_version }}-linux-amd64-local-installer.deb + asset_name: cortex-${{ inputs.new_version }}-linux-${{ inputs.arch }}-local-installer.deb asset_content_type: application/octet-stream \ No newline at end of file diff --git a/engine/Makefile b/engine/Makefile index 8f27eebcc..a6a4b5a79 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -24,6 +24,7 @@ DESTINATION_BINARY_SERVER_NAME ?= cortex-server DATA_FOLDER_NAME ?= .cortex CONFIGURATION_FILE_NAME ?= .cortexrc UNINSTALLER_FILE_NAME ?= cortex-uninstall.sh +ARCH ?= amd64 # Default target, does nothing all: @@ -120,7 +121,7 @@ else ifeq ($(shell uname -s),Linux) @echo "Building installer for linux"; \ cd templates/linux; \ chmod +x create_deb.sh; \ - ./create_deb.sh $(PACKAGE_NAME) $(VERSION) $(SOURCE_BINARY_PATH) $(SOURCE_BINARY_SERVER_PATH) $(DESTINATION_BINARY_NAME) $(DESTINATION_BINARY_SERVER_NAME) $(DATA_FOLDER_NAME) $(CONFIGURATION_FILE_NAME); \ + ./create_deb.sh $(PACKAGE_NAME) $(VERSION) $(SOURCE_BINARY_PATH) $(SOURCE_BINARY_SERVER_PATH) $(DESTINATION_BINARY_NAME) $(DESTINATION_BINARY_SERVER_NAME) $(DATA_FOLDER_NAME) $(CONFIGURATION_FILE_NAME) $(ARCH); \ cp $(PACKAGE_NAME).deb ../../ else @echo "Building installer for Macos"; \ diff --git a/engine/e2e-test/test_api_engine.py b/engine/e2e-test/test_api_engine.py index 57b47b879..e652e4495 100644 --- a/engine/e2e-test/test_api_engine.py +++ b/engine/e2e-test/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.35-27.10.24", "variant": "linux-amd64-avx-cuda-11-7"} + data = {"version": "v0.1.40-b4354", "variant": "linux-amd64-avx-cuda-11-7"} 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.35-27.10.24"} + data = {"version": "v0.1.40-b4354"} response = requests.post( "http://localhost:3928/v1/engines/llama-cpp/install", json=data ) diff --git a/engine/e2e-test/test_api_engine_update.py b/engine/e2e-test/test_api_engine_update.py index 23939f038..be8685dba 100644 --- a/engine/e2e-test/test_api_engine_update.py +++ b/engine/e2e-test/test_api_engine_update.py @@ -25,7 +25,7 @@ def setup_and_teardown(self): @pytest.mark.asyncio async def test_engines_update_should_be_successfully(self): - requests.post("http://localhost:3928/v1/engines/llama-cpp?version=0.1.34") + requests.post("http://localhost:3928/v1/engines/llama-cpp?version=0.1.43") response = requests.post("http://localhost:3928/v1/engines/llama-cpp/update") assert response.status_code == 200 diff --git a/engine/e2e-test/test_api_model.py b/engine/e2e-test/test_api_model.py index 8f2e4b07a..d75aa6831 100644 --- a/engine/e2e-test/test_api_model.py +++ b/engine/e2e-test/test_api_model.py @@ -85,6 +85,7 @@ async def test_model_pull_with_direct_url_should_have_desired_name(self): ], ) + @pytest.mark.asyncio async def test_models_start_stop_should_be_successful(self): print("Install engine") response = requests.post("http://localhost:3928/v1/engines/llama-cpp/install") diff --git a/engine/e2e-test/test_cli_engine_install.py b/engine/e2e-test/test_cli_engine_install.py index a998f3183..dbbc16e8a 100644 --- a/engine/e2e-test/test_cli_engine_install.py +++ b/engine/e2e-test/test_cli_engine_install.py @@ -49,7 +49,7 @@ def test_engines_install_onnx_on_tensorrt_should_be_failed(self): @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.29" + engine_version = "v0.1.43" exit_code, output, error = run( "Install Engine", ["engines", "install", "llama-cpp", "-v", engine_version], @@ -69,7 +69,7 @@ def test_engines_install_pre_release_llamacpp(self): assert is_engine_version_exist, f"Engine version {engine_version} is not found" assert exit_code == 0, f"Install engine failed with error: {error}" - @pytest.mark.skipif(platform.system() == "Windows", reason="Progress bar log issue on Windows") + @pytest.mark.skipif(platform.system() == "Windows" or platform.system() == "Linux", reason="Progress bar log issue on Windows") def test_engines_should_fallback_to_download_llamacpp_engine_if_not_exists(self): exit_code, output, error = run( "Install Engine", diff --git a/engine/e2e-test/test_cli_engine_install_nightly.py b/engine/e2e-test/test_cli_engine_install_nightly.py index 8c66c284c..bbb56ac9b 100644 --- a/engine/e2e-test/test_cli_engine_install_nightly.py +++ b/engine/e2e-test/test_cli_engine_install_nightly.py @@ -47,6 +47,7 @@ def test_engines_install_onnx_on_tensorrt_should_be_failed(self): 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() == "Linux", reason="Wait for linux arm ready") def test_engines_should_fallback_to_download_llamacpp_engine_if_not_exists(self): exit_code, output, error = run( "Install Engine", diff --git a/engine/e2e-test/test_cli_engine_uninstall.py b/engine/e2e-test/test_cli_engine_uninstall.py index fcc5f5c73..6b640d45d 100644 --- a/engine/e2e-test/test_cli_engine_uninstall.py +++ b/engine/e2e-test/test_cli_engine_uninstall.py @@ -24,7 +24,7 @@ def setup_and_teardown(self): @pytest.mark.asyncio async def test_engines_uninstall_llamacpp_should_be_successfully(self): - requests.post("http://127.0.0.1:3928/v1/engines/llama-cpp/install") + response = requests.post("http://localhost:3928/v1/engines/llama-cpp/install") await wait_for_websocket_download_success_event(timeout=None) exit_code, output, error = run( "Uninstall engine", ["engines", "uninstall", "llama-cpp"] diff --git a/engine/services/download_service.cc b/engine/services/download_service.cc index d855c8f61..a38dbe70e 100644 --- a/engine/services/download_service.cc +++ b/engine/services/download_service.cc @@ -140,10 +140,10 @@ cpp::result DownloadService::GetFileSize( curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); auto headers = curl_utils::GetHeaders(url); - if (headers.has_value()) { + if (headers) { curl_slist* curl_headers = nullptr; - for (const auto& [key, value] : headers.value()) { + for (const auto& [key, value] : headers->m) { auto header = key + ": " + value; curl_headers = curl_slist_append(curl_headers, header.c_str()); } @@ -227,10 +227,10 @@ cpp::result DownloadService::Download( curl_easy_setopt(curl, CURLOPT_URL, download_item.downloadUrl.c_str()); auto headers = curl_utils::GetHeaders(download_item.downloadUrl); - if (headers.has_value()) { + if (headers) { curl_slist* curl_headers = nullptr; - for (const auto& [key, value] : headers.value()) { + for (const auto& [key, value] : headers->m) { auto header = key + ": " + value; curl_headers = curl_slist_append(curl_headers, header.c_str()); } @@ -469,7 +469,7 @@ void DownloadService::SetUpCurlHandle(CURL* handle, const DownloadItem& item, auto headers = curl_utils::GetHeaders(item.downloadUrl); if (headers) { curl_slist* curl_headers = nullptr; - for (const auto& [key, value] : headers.value()) { + for (const auto& [key, value] : headers->m) { curl_headers = curl_slist_append(curl_headers, (key + ": " + value).c_str()); } diff --git a/engine/templates/linux/control b/engine/templates/linux/control index e877fe5ab..7c129a690 100644 --- a/engine/templates/linux/control +++ b/engine/templates/linux/control @@ -2,7 +2,7 @@ Package: $PACKAGE_NAME Version: $VERSION Section: base Priority: optional -Architecture: amd64 +Architecture: $ARCH Depends: openmpi-bin,libopenmpi-dev Maintainer: Homebrew Computer Pte Ltd Description: Cortex diff --git a/engine/templates/linux/create_deb.sh b/engine/templates/linux/create_deb.sh index 29492bdd8..f9247972f 100644 --- a/engine/templates/linux/create_deb.sh +++ b/engine/templates/linux/create_deb.sh @@ -6,6 +6,7 @@ DESTINATION_BINARY_NAME=$5 DESTINATION_BINARY_SERVER_NAME=$6 DATA_FOLDER_NAME=$7 CONFIGURATION_FILE_NAME=$8 +ARCH=$9 mkdir -p $PACKAGE_NAME/DEBIAN @@ -31,8 +32,9 @@ chmod 755 $PACKAGE_NAME/DEBIAN/postinst chmod 755 $PACKAGE_NAME/DEBIAN/postrm chmod 755 $PACKAGE_NAME/DEBIAN/prerm -export PACKAGE_NAME VERSION +export PACKAGE_NAME VERSION ARCH envsubst < control > $PACKAGE_NAME/DEBIAN/control +sed -i "s/ARCH/$ARCH/" $PACKAGE_NAME/DEBIAN/control dpkg-deb --build $PACKAGE_NAME $PACKAGE_NAME.deb diff --git a/engine/templates/linux/create_deb_local.sh b/engine/templates/linux/create_deb_local.sh index 6b54dc19d..0ab0c79c5 100644 --- a/engine/templates/linux/create_deb_local.sh +++ b/engine/templates/linux/create_deb_local.sh @@ -6,6 +6,7 @@ DESTINATION_BINARY_NAME=$5 DESTINATION_BINARY_SERVER_NAME=$6 DATA_FOLDER_NAME=$7 CONFIGURATION_FILE_NAME=$8 +ARCH=$9 mkdir -p $PACKAGE_NAME/DEBIAN @@ -34,7 +35,7 @@ chmod 755 $PACKAGE_NAME/DEBIAN/postinst chmod 755 $PACKAGE_NAME/DEBIAN/postrm chmod 755 $PACKAGE_NAME/DEBIAN/prerm -export PACKAGE_NAME VERSION +export PACKAGE_NAME VERSION ARCH envsubst < control > $PACKAGE_NAME/DEBIAN/control diff --git a/engine/templates/linux/install.sh b/engine/templates/linux/install.sh index e11b879c6..ade0b134a 100644 --- a/engine/templates/linux/install.sh +++ b/engine/templates/linux/install.sh @@ -6,6 +6,17 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi +# Determine architecture +ARCH=$(uname -m) +if [ "$ARCH" = "x86_64" ]; then + ARCH="amd64" +elif [ "$ARCH" = "aarch64" ]; then + ARCH="arm64" +else + echo "Unsupported architecture: $ARCH" + exit 1 +fi + # Determine the home directory based on the user USER_TO_RUN_AS=${SUDO_USER:-$(whoami)} if [ "$USER_TO_RUN_AS" = "root" ]; then @@ -142,19 +153,19 @@ install_cortex() { case $channel in stable) - url_binary="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-amd64.tar.gz" - url_deb_local="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-amd64-local-installer.deb" - url_deb_network="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-amd64-network-installer.deb" + url_binary="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-${ARCH}.tar.gz" + url_deb_local="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-${ARCH}-local-installer.deb" + url_deb_network="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-${ARCH}-network-installer.deb" ;; beta) - url_binary="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-amd64.tar.gz" - url_deb_local="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-amd64-local-installer.deb" - url_deb_network="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-amd64-network-installer.deb" + url_binary="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-${ARCH}.tar.gz" + url_deb_local="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-${ARCH}-local-installer.deb" + url_deb_network="https://github.com/janhq/cortex.cpp/releases/download/v${version}/cortex-${version}-linux-${ARCH}-network-installer.deb" ;; nightly) - url_binary="https://delta.jan.ai/cortex/v${version}/linux-amd64/cortex-nightly.tar.gz" - url_deb_local="https://delta.jan.ai/cortex/v${version}/linux-amd64/cortex-${version}-linux-amd64-local-installer.deb" - url_deb_network="https://delta.jan.ai/cortex/v${version}/linux-amd64/cortex-${version}-linux-amd64-network-installer.deb" + url_binary="https://delta.jan.ai/cortex/v${version}/linux-${ARCH}/cortex-nightly.tar.gz" + url_deb_local="https://delta.jan.ai/cortex/v${version}/linux-${ARCH}/cortex-${version}-linux-${ARCH}-local-installer.deb" + url_deb_network="https://delta.jan.ai/cortex/v${version}/linux-${ARCH}/cortex-${version}-linux-${ARCH}-network-installer.deb" ;; esac diff --git a/engine/test/components/test_engine_matcher_utils.cc b/engine/test/components/test_engine_matcher_utils.cc index 7da4e3cd1..1d1ed47a8 100644 --- a/engine/test/components/test_engine_matcher_utils.cc +++ b/engine/test/components/test_engine_matcher_utils.cc @@ -19,6 +19,7 @@ class EngineMatcherUtilsTestSuite : public ::testing::Test { "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", @@ -134,6 +135,18 @@ TEST_F(EngineMatcherUtilsTestSuite, TestValidate) { EXPECT_EQ(variant, "cortex.llamacpp-0.1.25-25.08.24-windows-amd64-avx2.tar.gz"); } + + { + auto os{"linux"}; + 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, "cortex.llamacpp-0.1.43-linux-arm64.tar.gz"); + } } TEST_F(EngineMatcherUtilsTestSuite, TestGetVersionAndArch) { diff --git a/engine/utils/curl_utils.cc b/engine/utils/curl_utils.cc index d5945e8c8..2481658ad 100644 --- a/engine/utils/curl_utils.cc +++ b/engine/utils/curl_utils.cc @@ -9,12 +9,24 @@ namespace curl_utils { namespace { -size_t WriteCallback(void* contents, size_t size, size_t nmemb, - std::string* output) { - size_t totalSize = size * nmemb; - output->append((char*)contents, totalSize); - return totalSize; -} +class CurlResponse { + public: + static size_t WriteCallback(char* buffer, size_t size, size_t nitems, + void* userdata) { + auto* response = static_cast(userdata); + return response->Append(buffer, size * nitems); + } + + size_t Append(const char* buffer, size_t size) { + data_.append(buffer, size); + return size; + } + + const std::string& GetData() const { return data_; } + + private: + std::string data_; +}; void SetUpProxy(CURL* handle, const std::string& url) { auto config = file_manager_utils::GetCortexConfig(); @@ -59,19 +71,18 @@ void SetUpProxy(CURL* handle, const std::string& url) { } } // namespace -std::optional> GetHeaders( - const std::string& url) { +std::shared_ptr
GetHeaders(const std::string& url) { auto url_obj = url_parser::FromUrlString(url); if (url_obj.has_error()) { - return std::nullopt; + return nullptr; } if (url_obj->host == kHuggingFaceHost) { - std::unordered_map headers{}; - headers["Content-Type"] = "application/json"; + auto headers = std::make_shared
(); + headers->m["Content-Type"] = "application/json"; auto const& token = file_manager_utils::GetCortexConfig().huggingFaceToken; if (!token.empty()) { - headers["Authorization"] = "Bearer " + token; + headers->m["Authorization"] = "Bearer " + token; // for debug purpose auto min_token_size = 6; @@ -87,15 +98,15 @@ std::optional> GetHeaders( } if (url_obj->host == kGitHubHost) { - std::unordered_map headers{}; - headers["Accept"] = "application/vnd.github.v3+json"; + auto headers = std::make_shared
(); + headers->m["Accept"] = "application/vnd.github.v3+json"; // github API requires user-agent https://docs.github.com/en/rest/using-the-rest-api/getting-started-with-the-rest-api?apiVersion=2022-11-28#user-agent auto user_agent = file_manager_utils::GetCortexConfig().gitHubUserAgent; auto gh_token = file_manager_utils::GetCortexConfig().gitHubToken; - headers["User-Agent"] = + headers->m["User-Agent"] = user_agent.empty() ? kDefaultGHUserAgent : user_agent; if (!gh_token.empty()) { - headers["Authorization"] = "Bearer " + gh_token; + headers->m["Authorization"] = "Bearer " + gh_token; // for debug purpose auto min_token_size = 6; @@ -109,7 +120,7 @@ std::optional> GetHeaders( return headers; } - return std::nullopt; + return nullptr; } cpp::result SimpleGet(const std::string& url, @@ -122,8 +133,8 @@ cpp::result SimpleGet(const std::string& url, auto headers = GetHeaders(url); curl_slist* curl_headers = nullptr; - if (headers.has_value()) { - for (const auto& [key, value] : headers.value()) { + if (headers) { + for (const auto& [key, value] : headers->m) { auto header = key + ": " + value; curl_headers = curl_slist_append(curl_headers, header.c_str()); } @@ -131,12 +142,14 @@ cpp::result SimpleGet(const std::string& url, curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); } - std::string readBuffer; + auto* response = new CurlResponse(); + std::shared_ptr s(response, + std::default_delete()); SetUpProxy(curl, url); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlResponse::WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); if (timeout > 0) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); } @@ -155,10 +168,10 @@ cpp::result SimpleGet(const std::string& url, if (http_code >= 400) { CTL_ERR("HTTP request failed with status code: " + std::to_string(http_code)); - return cpp::fail(readBuffer); + return cpp::fail(response->GetData()); } - return readBuffer; + return response->GetData(); } cpp::result SimpleRequest( @@ -176,13 +189,15 @@ cpp::result SimpleRequest( curl_slist_append(curl_headers, "Content-Type: application/json"); curl_headers = curl_slist_append(curl_headers, "Expect:"); - if (headers.has_value()) { - for (const auto& [key, value] : headers.value()) { + if (headers) { + for (const auto& [key, value] : headers->m) { auto header = key + ": " + value; curl_headers = curl_slist_append(curl_headers, header.c_str()); } } - std::string readBuffer; + auto* response = new CurlResponse(); + std::shared_ptr s(response, + std::default_delete()); SetUpProxy(curl, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); @@ -196,8 +211,8 @@ cpp::result SimpleRequest( curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); } curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlResponse::WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); @@ -221,10 +236,10 @@ cpp::result SimpleRequest( if (http_code >= 400) { CTL_ERR("HTTP request failed with status code: " + std::to_string(http_code)); - return cpp::fail(readBuffer); + return cpp::fail(response->GetData()); } - return readBuffer; + return response->GetData(); } cpp::result ReadRemoteYaml(const std::string& url) { diff --git a/engine/utils/curl_utils.h b/engine/utils/curl_utils.h index f33b7e8e5..9035b6b3c 100644 --- a/engine/utils/curl_utils.h +++ b/engine/utils/curl_utils.h @@ -15,8 +15,11 @@ enum class RequestType { GET, PATCH, POST, DEL }; namespace curl_utils { -std::optional> GetHeaders( - const std::string& url); +struct Header { + std::unordered_map m; +}; + +std::shared_ptr
GetHeaders(const std::string& url); cpp::result SimpleGet(const std::string& url, const int timeout = -1); diff --git a/engine/utils/engine_matcher_utils.h b/engine/utils/engine_matcher_utils.h index a6135e532..28c0f0c2a 100644 --- a/engine/utils/engine_matcher_utils.h +++ b/engine/utils/engine_matcher_utils.h @@ -156,6 +156,11 @@ inline std::string Validate(const std::vector& variants, if (os == "mac" && !os_and_arch_compatible_list.empty()) return os_and_arch_compatible_list[0]; + if (os == "linux" && cpu_arch == "arm64" && + !os_and_arch_compatible_list.empty()) { + return os_and_arch_compatible_list[0]; + } + std::vector avx_compatible_list; std::copy_if(os_and_arch_compatible_list.begin(),