Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 07cf2ff

Browse files
authored
feat: cortex-cpp node addon (#852)
1 parent 2c880d3 commit 07cf2ff

File tree

17 files changed

+372
-212
lines changed

17 files changed

+372
-212
lines changed

.github/workflows/cortex-build.yml

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,29 @@ jobs:
4545
runs-on: "ubuntu-20-04"
4646
cmake-flags: ""
4747
build-deps-cmake-flags: ""
48-
ccache-dir: ''
48+
arch: "x64"
49+
platform: "linux"
4950
- os: "mac"
5051
name: "amd64"
5152
runs-on: "macos-13"
5253
cmake-flags: ""
5354
build-deps-cmake-flags: ""
54-
ccache-dir: ''
55+
arch: "x64"
56+
platform: "darwin"
5557
- os: "mac"
5658
name: "arm64"
5759
runs-on: "macos-latest"
5860
cmake-flags: "-DMAC_ARM64=ON"
5961
build-deps-cmake-flags: ""
60-
ccache-dir: ''
62+
arch: "arm64"
63+
platform: "darwin"
6164
- os: "windows"
6265
name: "amd64"
6366
runs-on: "windows-cuda-12-0"
6467
cmake-flags: "-DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache -GNinja"
6568
build-deps-cmake-flags: "-DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache -GNinja"
66-
ccache-dir: 'C:\Users\ContainerAdministrator\AppData\Local\ccache'
69+
arch: "x64"
70+
platform: "win32"
6771

6872
steps:
6973
- name: Clone
@@ -77,6 +81,11 @@ jobs:
7781
with:
7882
dotnet-version: "8.0.x"
7983

84+
- uses: actions/setup-node@v3
85+
with:
86+
node-version: "20.x"
87+
registry-url: "https://registry.npmjs.org"
88+
8089
- name: Install choco on Windows
8190
if: runner.os == 'Windows'
8291
run: |
@@ -116,7 +125,7 @@ jobs:
116125
run: |
117126
cd cortex-cpp
118127
make pre-package
119-
128+
120129
- name: Code Signing macOS
121130
if: runner.os == 'macOS'
122131
run: |
@@ -163,6 +172,50 @@ jobs:
163172
AWS_SECRET_ACCESS_KEY: "${{ secrets.MINIO_SECRET_ACCESS_KEY }}"
164173
AWS_DEFAULT_REGION: "${{ secrets.MINIO_REGION }}"
165174

175+
## cortex-cpp node binding
176+
177+
# update version in package.json
178+
- name: Install jq
179+
uses: dcarbone/install-jq-action@v2.0.1
180+
181+
- name: "Update version by tag"
182+
working-directory: cortex-cpp
183+
run: |
184+
echo "Version: ${{ needs.create-draft-release.outputs.version }}"
185+
# Update the version in package.json
186+
jq --arg version "${{ needs.create-draft-release.outputs.version }}" '.version = $version' package.json > package-tmp.json
187+
rm package.json
188+
mv package-tmp.json package.json
189+
190+
# build prebuilds
191+
- name: Build Prebuilds
192+
working-directory: cortex-cpp
193+
run: |
194+
npm install -g yarn
195+
yarn && yarn prebuild
196+
197+
# upload prebuilds
198+
- name: Upload Prebuilds Darwin
199+
uses: actions/upload-release-asset@v1.0.1
200+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
201+
env:
202+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
203+
with:
204+
upload_url: ${{ needs.create-draft-release.outputs.upload_url }}
205+
asset_path: ./cortex-cpp/prebuilds/cortex-cpp-v${{ needs.create-draft-release.outputs.version }}-napi-v8-${{matrix.platform}}-${{ matrix.arch }}.tar.gz
206+
asset_name: cortex-cpp-v${{ needs.create-draft-release.outputs.version }}-napi-v8-${{matrix.platform}}-${{ matrix.arch }}.tar.gz
207+
asset_content_type: application/gzip
208+
209+
# Setup .npmrc file to publish to npm - upload only once
210+
- run: npm publish --access public
211+
continue-on-error: true
212+
if: runner.os == 'linux'
213+
env:
214+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
215+
working-directory: ./cortex-cpp
216+
217+
## cortex-cpp node binding
218+
166219
build-cortex-single-binary:
167220
runs-on: ${{ matrix.runs-on }}
168221
needs: [create-draft-release]
@@ -229,7 +282,7 @@ jobs:
229282
with:
230283
python-version: "3.10"
231284

232-
- run: pip3 install --upgrade setuptools
285+
- run: pip3 install --upgrade setuptools
233286
if: runner.os == 'macOS'
234287

235288
- run: yarn install && yarn build:binary

cortex-cpp/CMakeLists.txt

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
cmake_minimum_required(VERSION 3.5)
2+
23
project(cortex-cpp C CXX)
34

5+
# Build using CMAKE-JS
6+
if(DEFINED CMAKE_JS_INC)
7+
include_directories(${CMAKE_JS_INC})
8+
endif()
9+
410
include(CheckIncludeFileCXX)
511

612
check_include_file_cxx(any HAS_ANY)
@@ -53,13 +59,29 @@ if(APPLE)
5359
endif()
5460
endif()
5561

62+
if(DEFINED CMAKE_JS_INC)
63+
# define NPI_VERSION
64+
add_compile_definitions(NAPI_VERSION=8)
65+
endif()
66+
5667
add_compile_definitions(CORTEX_CPP_VERSION="${CORTEX_CPP_VERSION}")
5768

5869
# add_subdirectory(test)
5970

60-
add_executable(${PROJECT_NAME} main.cc
61-
${CMAKE_CURRENT_SOURCE_DIR}/utils/cpuid/cpu_info.cc
62-
)
71+
# Build using CMAKE-JS
72+
if(DEFINED CMAKE_JS_INC)
73+
if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"))
74+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
75+
endif()
76+
77+
add_library(${PROJECT_NAME} SHARED addon.cc
78+
${CMAKE_CURRENT_SOURCE_DIR}/utils/cpuid/cpu_info.cc
79+
)
80+
else() # Official build
81+
add_executable(${PROJECT_NAME} main.cc
82+
${CMAKE_CURRENT_SOURCE_DIR}/utils/cpuid/cpu_info.cc
83+
)
84+
endif()
6385

6486
# ##############################################################################
6587
# If you include the drogon source code locally in your project, use this method
@@ -69,9 +91,23 @@ add_executable(${PROJECT_NAME} main.cc
6991
# and comment out the following lines
7092

7193
find_package(Drogon CONFIG REQUIRED)
72-
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon
73-
${CMAKE_THREAD_LIBS_INIT})
7494

95+
96+
# Build using CMAKE-JS
97+
if(DEFINED CMAKE_JS_INC)
98+
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
99+
100+
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon
101+
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_JS_LIB})
102+
103+
if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET)
104+
# Generate node.lib
105+
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
106+
endif()
107+
else()
108+
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon
109+
${CMAKE_THREAD_LIBS_INIT})
110+
endif()
75111
# ##############################################################################
76112

77113
if(CMAKE_CXX_STANDARD LESS 17)
@@ -103,4 +139,4 @@ target_sources(${PROJECT_NAME} PRIVATE ${CTL_SRC} ${COMMON_SRC})
103139
# ${FILTER_SRC} ${PLUGIN_SRC} ${MODEL_SRC})
104140
# ##############################################################################
105141
# uncomment the following line for dynamically loading views set_property(TARGET
106-
# ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)
142+
# ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)

cortex-cpp/addon.cc

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#include <napi.h>
2+
3+
#include <drogon/HttpAppFramework.h>
4+
#include <drogon/drogon.h>
5+
#include <stdlib.h>
6+
#include <climits> // for PATH_MAX
7+
#include <iostream>
8+
#include "cortex-common/cortexpythoni.h"
9+
#include "utils/cortex_utils.h"
10+
#include "utils/dylib.h"
11+
12+
#if defined(__APPLE__) && defined(__MACH__)
13+
#include <libgen.h> // for dirname()
14+
#include <mach-o/dyld.h>
15+
#elif defined(__linux__)
16+
#include <libgen.h> // for dirname()
17+
#include <unistd.h> // for readlink()
18+
#elif defined(_WIN32)
19+
#include <windows.h>
20+
#undef max
21+
#else
22+
#error "Unsupported platform!"
23+
#endif
24+
25+
static Napi::Env* s_env = nullptr;
26+
27+
void start() {
28+
int thread_num = 1;
29+
std::string host = "127.0.0.1";
30+
int port = 3929;
31+
std::string uploads_folder_path;
32+
int logical_cores = std::thread::hardware_concurrency();
33+
int drogon_thread_num = std::max(thread_num, logical_cores);
34+
#ifdef CORTEX_CPP_VERSION
35+
LOG_INFO << "cortex-cpp version: " << CORTEX_CPP_VERSION;
36+
#else
37+
LOG_INFO << "cortex-cpp version: undefined";
38+
#endif
39+
#ifdef CORTEX_LLAMACPP_VERSION
40+
LOG_INFO << "cortex.llamacpp version: " << CORTEX_LLAMACPP_VERSION;
41+
#endif
42+
43+
LOG_INFO << "Server started, listening at: " << host << ":" << port;
44+
LOG_INFO << "Please load your model";
45+
drogon::app().addListener(host, port);
46+
drogon::app().setThreadNum(drogon_thread_num);
47+
if (!uploads_folder_path.empty()) {
48+
LOG_INFO << "Drogon uploads folder is at: " << uploads_folder_path;
49+
drogon::app().setUploadPath(uploads_folder_path);
50+
}
51+
LOG_INFO << "Number of thread is:" << drogon::app().getThreadNum();
52+
53+
drogon::app().run();
54+
}
55+
56+
void stop() {
57+
drogon::app().quit();
58+
}
59+
60+
void exitCallback() {
61+
Napi::TypeError::New(*s_env, "Process Exited!").ThrowAsJavaScriptException();
62+
}
63+
64+
Napi::Value Start(const Napi::CallbackInfo& info) {
65+
Napi::Env env = info.Env();
66+
67+
s_env = &env;
68+
69+
// Register exitCallback with atexit
70+
std::atexit(exitCallback);
71+
72+
start();
73+
return env.Undefined();
74+
}
75+
76+
Napi::Value Stop(const Napi::CallbackInfo& info) {
77+
Napi::Env env = info.Env();
78+
stop();
79+
return Napi::String::New(env, "Server stopped successfully");
80+
}
81+
82+
Napi::Object Init(Napi::Env env, Napi::Object exports) {
83+
exports.Set(Napi::String::New(env, "start"), Napi::Function::New(env, Start));
84+
exports.Set(Napi::String::New(env, "stop"), Napi::Function::New(env, Start));
85+
return exports;
86+
}
87+
88+
NODE_API_MODULE(cortex-cpp, Init)

cortex-cpp/binding/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Type definitions for cortex-cpp node binding
2+
3+
/// <reference types="node" />
4+
declare module "cortex-cpp" {
5+
export function start();
6+
export function stop();
7+
}

cortex-cpp/binding/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const addon = require("./../build/Release/cortex-cpp.node");
2+
3+
module.exports = addon;

cortex-cpp/controllers/server.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,9 @@ void server::FineTuning(
206206
if (engines_.find(engine_type) == engines_.end()) {
207207
try {
208208
std::string abs_path =
209-
cortex_utils::GetCurrentPath() + cortex_utils::kPythonRuntimeLibPath;
209+
(getenv("ENGINE_PATH") ? getenv("ENGINE_PATH")
210+
: cortex_utils::GetCurrentPath()) +
211+
cortex_utils::kPythonRuntimeLibPath;
210212
engines_[engine_type].dl =
211213
std::make_unique<cortex_cpp::dylib>(abs_path, "engine");
212214
} catch (const cortex_cpp::dylib::load_error& e) {
@@ -262,9 +264,9 @@ void server::LoadModel(const HttpRequestPtr& req,
262264
auto get_engine_path = [](std::string_view e) {
263265
if (e == kLlamaEngine) {
264266
return cortex_utils::kLlamaLibPath;
265-
} else if(e == kOnnxEngine) {
267+
} else if (e == kOnnxEngine) {
266268
return cortex_utils::kOnnxLibPath;
267-
} else if(e == kTensorrtLlmEngine) {
269+
} else if (e == kTensorrtLlmEngine) {
268270
return cortex_utils::kTensorrtLlmPath;
269271
}
270272
return cortex_utils::kLlamaLibPath;
@@ -277,7 +279,9 @@ void server::LoadModel(const HttpRequestPtr& req,
277279
}
278280

279281
std::string abs_path =
280-
cortex_utils::GetCurrentPath() + get_engine_path(engine_type);
282+
(getenv("ENGINE_PATH") ? getenv("ENGINE_PATH")
283+
: cortex_utils::GetCurrentPath()) +
284+
get_engine_path(engine_type);
281285
engines_[engine_type].dl =
282286
std::make_unique<cortex_cpp::dylib>(abs_path, "engine");
283287

cortex-cpp/cortex-cpp-deps/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ ExternalProject_Add(
6262
GIT_TAG cares-1_26_0
6363
CMAKE_ARGS
6464
-DCARES_SHARED=OFF
65-
-DCARES_STATIC=ON
65+
-DCARES_STATIC=ON
66+
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
6667
-DCMAKE_INSTALL_PREFIX=${THIRD_PARTY_INSTALL_PATH}
6768
-DCMAKE_BUILD_TYPE=RELEASE
69+
-DCARES_STATIC_PIC=ON
6870
)
6971

7072
ExternalProject_Add(

cortex-cpp/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "cortex-cpp",
3+
"version": "0.0.11",
4+
"description": "Cortex-cpp is a streamlined, stateless C++ server engineered to be fully compatible with OpenAI's API, particularly its stateless functionalities",
5+
"main": "./binding/index.js",
6+
"types": "./binding/index.d.ts",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/janhq/cortex.git"
10+
},
11+
"scripts": {
12+
"install": "prebuild-install --runtime napi --backend cmake-js --config Release || cmake-js rebuild --config Release",
13+
"build": "cmake-js configure --config Release && cmake-js build --config Release",
14+
"rebuild": "cmake-js rebuild --config Release",
15+
"prebuild": "prebuild --runtime napi --backend cmake-js --all --strip --verbose --config Release",
16+
"upload": "prebuild --runtime napi --backend cmake-js --upload ${GITHUB_TOKEN}"
17+
},
18+
"author": "Jan <service@jan.ai>",
19+
"license": "Apache-2.0",
20+
"gypfile": true,
21+
"dependencies": {
22+
"bindings": "^1.5.0",
23+
"cmake-js": "^7.3.0",
24+
"node-addon-api": "^7.0.0",
25+
"prebuild": "^13.0.1",
26+
"prebuild-install": "^7.1.2"
27+
},
28+
"devDependencies": {
29+
"@types/node": "^20.14.9",
30+
"typescript": "^5.5.3"
31+
},
32+
"binary": {
33+
"napi_versions": [
34+
8
35+
]
36+
},
37+
"files": [
38+
"binding/*.js",
39+
"binding/*.d.ts"
40+
]
41+
}

0 commit comments

Comments
 (0)