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

Commit 131b610

Browse files
authored
feat: cortex start command (#1235)
* feat: cortex start * fix: print app.help * feat: add -p, --port for cortex start * fix: check if model is started before start model * fix: correct binary name * fix: e2e test * fix: host, port from config * fix: correct condition * fix: correct url * fix: add retry check health status
1 parent a152a1a commit 131b610

File tree

9 files changed

+219
-103
lines changed

9 files changed

+219
-103
lines changed

engine/commands/chat_cmd.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include "chat_cmd.h"
22
#include "httplib.h"
33

4+
#include "cortex_upd_cmd.h"
45
#include "trantor/utils/Logger.h"
56
#include "utils/logging_utils.h"
7+
#include "server_start_cmd.h"
68

79
namespace commands {
810
namespace {
@@ -33,6 +35,15 @@ ChatCmd::ChatCmd(std::string host, int port, const config::ModelConfig& mc)
3335
: host_(std::move(host)), port_(port), mc_(mc) {}
3436

3537
void ChatCmd::Exec(std::string msg) {
38+
// Check if server is started
39+
{
40+
if (!commands::IsServerAlive(host_, port_)) {
41+
CLI_LOG("Server is not started yet, please run `"
42+
<< commands::GetCortexBinary() << " start` to start server!");
43+
return;
44+
}
45+
}
46+
3647
auto address = host_ + ":" + std::to_string(port_);
3748
// Check if model is loaded
3849
// TODO(sang) only llamacpp support modelstatus for now

engine/commands/cortex_upd_cmd.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void CortexUpdCmd::Exec(std::string v) {
1515
{
1616
auto config = file_manager_utils::GetCortexConfig();
1717
httplib::Client cli(config.apiServerHost + ":" + config.apiServerPort);
18-
auto res = cli.Get("/health/healthz");
18+
auto res = cli.Get("/healthz");
1919
if (res) {
2020
CLI_LOG("Server is running. Stopping server before updating!");
2121
commands::ServerStopCmd ssc(config.apiServerHost,

engine/commands/model_start_cmd.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "model_start_cmd.h"
2+
#include "cortex_upd_cmd.h"
23
#include "httplib.h"
34
#include "nlohmann/json.hpp"
5+
#include "server_start_cmd.h"
46
#include "trantor/utils/Logger.h"
7+
#include "utils/file_manager_utils.h"
58
#include "utils/logging_utils.h"
69

710
namespace commands {
@@ -10,7 +13,15 @@ ModelStartCmd::ModelStartCmd(std::string host, int port,
1013
: host_(std::move(host)), port_(port), mc_(mc) {}
1114

1215
bool ModelStartCmd::Exec() {
16+
// Check if server is started
17+
if (!commands::IsServerAlive(host_, port_)) {
18+
CLI_LOG("Server is not started yet, please run `"
19+
<< commands::GetCortexBinary() << " start` to start server!");
20+
return false;
21+
}
22+
1323
httplib::Client cli(host_ + ":" + std::to_string(port_));
24+
1425
nlohmann::json json_data;
1526
if (mc_.files.size() > 0) {
1627
// TODO(sang) support multiple files

engine/commands/run_cmd.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
#include "chat_cmd.h"
33
#include "cmd_info.h"
44
#include "config/yaml_config.h"
5+
#include "engine_install_cmd.h"
6+
#include "httplib.h"
7+
#include "model_pull_cmd.h"
58
#include "model_start_cmd.h"
9+
#include "server_start_cmd.h"
10+
#include "trantor/utils/Logger.h"
11+
#include "utils/cortex_utils.h"
612
#include "utils/file_manager_utils.h"
713

814
namespace commands {
@@ -15,7 +21,7 @@ void RunCmd::Exec() {
1521
// TODO should we clean all resource if something fails?
1622
// Check if model existed. If not, download it
1723
{
18-
auto model_conf = model_service_.GetDownloadedModel(model_id_);
24+
auto model_conf = model_service_.GetDownloadedModel(model_file + ".yaml");
1925
if (!model_conf.has_value()) {
2026
model_service_.DownloadModel(model_id_);
2127
}
@@ -35,6 +41,17 @@ void RunCmd::Exec() {
3541
}
3642
}
3743

44+
// Start server if it is not running
45+
{
46+
if (!commands::IsServerAlive(host_, port_)) {
47+
CLI_LOG("Starting server ...");
48+
commands::ServerStartCmd ssc;
49+
if(!ssc.Exec(host_, port_)) {
50+
return;
51+
}
52+
}
53+
}
54+
3855
// Start model
3956
config::YamlHandler yaml_handler;
4057
yaml_handler.ModelConfigFromFile(
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#include "server_start_cmd.h"
2+
#include "commands/cortex_upd_cmd.h"
3+
#include "httplib.h"
4+
#include "trantor/utils/Logger.h"
5+
#include "utils/cortex_utils.h"
6+
#include "utils/file_manager_utils.h"
7+
#include "utils/logging_utils.h"
8+
9+
namespace commands {
10+
11+
namespace {
12+
bool TryConnectToServer(const std::string& host, int port) {
13+
constexpr const auto kMaxRetry = 3u;
14+
auto count = 0u;
15+
// Check if server is started
16+
while (true) {
17+
if (IsServerAlive(host, port))
18+
break;
19+
// Wait for server up
20+
std::this_thread::sleep_for(std::chrono::seconds(1));
21+
if (count++ == kMaxRetry) {
22+
std::cerr << "Could not start server" << std::endl;
23+
return false;
24+
}
25+
}
26+
return true;
27+
}
28+
} // namespace
29+
30+
ServerStartCmd::ServerStartCmd() {}
31+
32+
bool ServerStartCmd::Exec(const std::string& host, int port) {
33+
#if defined(_WIN32) || defined(_WIN64)
34+
// Windows-specific code to create a new process
35+
STARTUPINFO si;
36+
PROCESS_INFORMATION pi;
37+
38+
ZeroMemory(&si, sizeof(si));
39+
si.cb = sizeof(si);
40+
ZeroMemory(&pi, sizeof(pi));
41+
auto exe = commands::GetCortexBinary();
42+
std::string cmds =
43+
cortex_utils::GetCurrentPath() + "/" + exe + " --start-server";
44+
// Create child process
45+
if (!CreateProcess(
46+
NULL, // No module name (use command line)
47+
const_cast<char*>(
48+
cmds.c_str()), // Command line (replace with your actual executable)
49+
NULL, // Process handle not inheritable
50+
NULL, // Thread handle not inheritable
51+
FALSE, // Set handle inheritance to FALSE
52+
0, // No creation flags
53+
NULL, // Use parent's environment block
54+
NULL, // Use parent's starting directory
55+
&si, // Pointer to STARTUPINFO structure
56+
&pi)) // Pointer to PROCESS_INFORMATION structure
57+
{
58+
std::cout << "Could not start server: " << GetLastError() << std::endl;
59+
return false;
60+
} else {
61+
if(!TryConnectToServer(host, port)) {
62+
return false;
63+
}
64+
std::cout << "Server started" << std::endl;
65+
}
66+
67+
#else
68+
// Unix-like system-specific code to fork a child process
69+
pid_t pid = fork();
70+
71+
if (pid < 0) {
72+
// Fork failed
73+
std::cerr << "Could not start server: " << std::endl;
74+
return false;
75+
} else if (pid == 0) {
76+
// No need to configure LD_LIBRARY_PATH for macOS
77+
#if !defined(__APPLE__) || !defined(__MACH__)
78+
const char* name = "LD_LIBRARY_PATH";
79+
auto data = getenv(name);
80+
std::string v;
81+
if (auto g = getenv(name); g) {
82+
v += g;
83+
}
84+
CTL_INF("LD_LIBRARY_PATH: " << v);
85+
auto data_path = file_manager_utils::GetCortexDataPath();
86+
auto llamacpp_path = data_path / "engines" / "cortex.llamacpp/";
87+
auto trt_path = data_path / "engines" / "cortex.tensorrt-llm/";
88+
auto new_v = trt_path.string() + ":" + llamacpp_path.string() + ":" + v;
89+
setenv(name, new_v.c_str(), true);
90+
CTL_INF("LD_LIBRARY_PATH: " << getenv(name));
91+
#endif
92+
auto exe = commands::GetCortexBinary();
93+
std::string p = cortex_utils::GetCurrentPath() + "/" + exe;
94+
execl(p.c_str(), exe.c_str(), "--start-server", (char*)0);
95+
} else {
96+
// Parent process
97+
if(!TryConnectToServer(host, port)) {
98+
return false;
99+
}
100+
std::cout << "Server started" << std::endl;
101+
}
102+
#endif
103+
return true;
104+
}
105+
106+
}; // namespace commands

engine/commands/server_start_cmd.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
#include <string>
3+
#include "httplib.h"
4+
5+
namespace commands {
6+
7+
inline bool IsServerAlive(const std::string& host, int port) {
8+
httplib::Client cli(host + ":" + std::to_string(port));
9+
auto res = cli.Get("/healthz");
10+
if (res && res->status == httplib::StatusCode::OK_200) {
11+
return true;
12+
}
13+
return false;
14+
}
15+
16+
class ServerStartCmd {
17+
public:
18+
ServerStartCmd();
19+
bool Exec(const std::string& host, int port);
20+
};
21+
} // namespace commands

engine/controllers/command_line_parser.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "commands/model_start_cmd.h"
1414
#include "commands/model_stop_cmd.h"
1515
#include "commands/run_cmd.h"
16+
#include "commands/server_start_cmd.h"
1617
#include "commands/server_stop_cmd.h"
1718
#include "config/yaml_config.h"
1819
#include "services/engine_service.h"
@@ -174,6 +175,21 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {
174175
});
175176
}
176177

178+
auto start_cmd = app_.add_subcommand("start", "Start the API server");
179+
int port = std::stoi(config.apiServerPort);
180+
start_cmd->add_option("-p, --port", port, "Server port to listen");
181+
start_cmd->callback([&config, &port] {
182+
if (port != stoi(config.apiServerPort)) {
183+
CTL_INF("apiServerPort changed from " << config.apiServerPort << " to "
184+
<< port);
185+
auto config_path = file_manager_utils::GetConfigurationPath();
186+
config.apiServerPort = std::to_string(port);
187+
config_yaml_utils::DumpYamlConfig(config, config_path.string());
188+
}
189+
commands::ServerStartCmd ssc;
190+
ssc.Exec(config.apiServerHost, std::stoi(config.apiServerPort));
191+
});
192+
177193
auto stop_cmd = app_.add_subcommand("stop", "Stop the API server");
178194

179195
stop_cmd->callback([&config] {
@@ -208,6 +224,10 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {
208224
}
209225

210226
CLI11_PARSE(app_, argc, argv);
227+
if (argc == 1) {
228+
CLI_LOG(app_.help());
229+
return true;
230+
}
211231

212232
// Check new update, only check for stable release for now
213233
#ifdef CORTEX_CPP_VERSION

engine/e2e-test/test_runner.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ def start_server() -> bool:
5050
def start_server_nix() -> bool:
5151
executable = getExecutablePath()
5252
process = subprocess.Popen(
53-
executable, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
53+
[executable] + ['start', '-p', '3928'],
54+
stdout=subprocess.PIPE,
55+
stderr=subprocess.PIPE,
56+
text=True
5457
)
5558

5659
start_time = time.time()
@@ -77,7 +80,7 @@ def start_server_nix() -> bool:
7780
def start_server_windows() -> bool:
7881
executable = getExecutablePath()
7982
process = subprocess.Popen(
80-
executable,
83+
[executable] + ['start', '-p', '3928'],
8184
stdout=subprocess.PIPE,
8285
stderr=subprocess.PIPE,
8386
text=True,

0 commit comments

Comments
 (0)