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

Commit 7f52abe

Browse files
author
Gabrielle Ong
authored
Merge branch 'dev' into feat/cli-docs-models-engines
2 parents a5c0172 + 55bbe0d commit 7f52abe

File tree

8 files changed

+69
-7
lines changed

8 files changed

+69
-7
lines changed

docs/docs/installation/mac.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Before installation, make sure that you have met the required [dependencies](#de
2222
- Stable: https://github.com/janhq/cortex.cpp/releases
2323
- Beta: https://github.com/janhq/cortex.cpp/releases
2424
- Nightly: https://github.com/janhq/cortex.cpp/releases
25-
2. Ensure that Cortex.cpp is sucessfulyy installed:
25+
2. Ensure that Cortex.cpp is sucessfully installed:
2626
```bash
2727
# Stable
2828
cortex

docs/docs/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Cortex.cpp allows users to pull models from multiple Model Hubs, offering flexib
5151
| Model /Engine | llama.cpp | Command |
5252
| -------------- | --------------------- | ----------------------------- |
5353
| phi-3.5 || cortex run phi3.5 |
54-
| llama3.2 || cortex run llama3.1 |
54+
| llama3.2 || cortex run llama3.2 |
5555
| llama3.1 || cortex run llama3.1 |
5656
| codestral || cortex run codestral |
5757
| gemma2 || cortex run gemma2 |

docs/static/openapi/cortex.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@
642642
"example": {
643643
"model": "model-id",
644644
"modelPath": "/path/to/gguf",
645-
"name": "model display name"
645+
"name": "model display name",
646+
"option": "symlink"
646647
}
647648
}
648649
}
@@ -3187,6 +3188,11 @@
31873188
"name": {
31883189
"type": "string",
31893190
"description": "The display name of the model."
3191+
},
3192+
"option": {
3193+
"type": "string",
3194+
"description": "Import options such as symlink or copy.",
3195+
"enum": ["symlink", "copy"]
31903196
}
31913197
},
31923198
"required": ["model", "modelPath"]
@@ -3434,6 +3440,11 @@
34343440
"description": "To enable mmap, default is true",
34353441
"example": true
34363442
},
3443+
"size": {
3444+
"type": "number",
3445+
"description": "The model file size in bytes",
3446+
"example": 1073741824
3447+
},
34373448
"engine": {
34383449
"type": "string",
34393450
"description": "The engine to use.",

engine/config/model_config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct ModelConfig {
5858
bool ignore_eos = false;
5959
int n_probs = 0;
6060
int min_keep = 0;
61+
uint64_t size = 0;
6162
std::string grammar;
6263

6364
void FromJson(const Json::Value& json) {
@@ -70,6 +71,8 @@ struct ModelConfig {
7071
// model = json["model"].asString();
7172
if (json.isMember("version"))
7273
version = json["version"].asString();
74+
if (json.isMember("size"))
75+
size = json["size"].asUInt64();
7376

7477
if (json.isMember("stop") && json["stop"].isArray()) {
7578
stop.clear();
@@ -176,6 +179,7 @@ struct ModelConfig {
176179
obj["name"] = name;
177180
obj["model"] = model;
178181
obj["version"] = version;
182+
obj["size"] = size;
179183

180184
Json::Value stop_array(Json::arrayValue);
181185
for (const auto& s : stop) {
@@ -269,6 +273,7 @@ struct ModelConfig {
269273
oss << format_utils::print_comment("END REQUIRED");
270274
oss << format_utils::print_comment("BEGIN OPTIONAL");
271275

276+
oss << format_utils::print_float("size", size);
272277
oss << format_utils::print_bool("stream", stream);
273278
oss << format_utils::print_float("top_p", top_p);
274279
oss << format_utils::print_float("temperature", temperature);

engine/config/yaml_config.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ void YamlHandler::ModelConfigFromYaml() {
7575
tmp.model = yaml_node_["model"].as<std::string>();
7676
if (yaml_node_["version"])
7777
tmp.version = yaml_node_["version"].as<std::string>();
78+
if (yaml_node_["size"])
79+
tmp.size = yaml_node_["size"].as<uint64_t>();
7880
if (yaml_node_["engine"])
7981
tmp.engine = yaml_node_["engine"].as<std::string>();
8082
if (yaml_node_["prompt_template"]) {
@@ -266,6 +268,8 @@ void YamlHandler::UpdateModelConfig(ModelConfig new_model_config) {
266268
if (!model_config_.grammar.empty())
267269
yaml_node_["grammar"] = model_config_.grammar;
268270

271+
yaml_node_["size"] = model_config_.size;
272+
269273
yaml_node_["created"] = std::time(nullptr);
270274
} catch (const std::exception& e) {
271275
std::cerr << "Error when update model config : " << e.what() << std::endl;
@@ -318,6 +322,7 @@ void YamlHandler::WriteYamlFile(const std::string& file_path) const {
318322
outFile << "# END REQUIRED\n";
319323
outFile << "\n";
320324
outFile << "# BEGIN OPTIONAL\n";
325+
outFile << format_utils::writeKeyValue("size", yaml_node_["size"]);
321326
outFile << format_utils::writeKeyValue("stream", yaml_node_["stream"],
322327
"Default true?");
323328
outFile << format_utils::writeKeyValue("top_p", yaml_node_["top_p"],

engine/controllers/models.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "database/models.h"
22
#include <drogon/HttpTypes.h>
3+
#include <filesystem>
34
#include <optional>
45
#include "config/gguf_parser.h"
56
#include "config/yaml_config.h"
@@ -320,6 +321,7 @@ void Models::ImportModel(
320321
auto modelHandle = (*(req->getJsonObject())).get("model", "").asString();
321322
auto modelPath = (*(req->getJsonObject())).get("modelPath", "").asString();
322323
auto modelName = (*(req->getJsonObject())).get("name", "").asString();
324+
auto option = (*(req->getJsonObject())).get("option", "symlink").asString();
323325
config::GGUFHandler gguf_handler;
324326
config::YamlHandler yaml_handler;
325327
cortex::db::Models modellist_utils_obj;
@@ -339,7 +341,19 @@ void Models::ImportModel(
339341
std::filesystem::path(model_yaml_path).parent_path());
340342
gguf_handler.Parse(modelPath);
341343
config::ModelConfig model_config = gguf_handler.GetModelConfig();
342-
model_config.files.push_back(modelPath);
344+
// There are 2 options: symlink and copy
345+
if (option == "copy") {
346+
// Copy GGUF file to the destination path
347+
std::filesystem::path file_path =
348+
std::filesystem::path(model_yaml_path).parent_path() /
349+
std::filesystem::path(modelPath).filename();
350+
std::filesystem::copy_file(
351+
modelPath, file_path,
352+
std::filesystem::copy_options::update_existing);
353+
model_config.files.push_back(file_path.string());
354+
} else {
355+
model_config.files.push_back(modelPath);
356+
}
343357
model_config.model = modelHandle;
344358
model_config.name = modelName.empty() ? model_config.name : modelName;
345359
yaml_handler.UpdateModelConfig(model_config);

engine/e2e-test/test_api_model_import.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ def test_model_import_with_name_should_be_success(self):
2929
response = requests.post("http://localhost:3928/models/import", json=body_json)
3030
assert response.status_code == 200
3131

32+
@pytest.mark.skipif(True, reason="Expensive test. Only test when you have local gguf file.")
33+
def test_model_import_with_name_should_be_success(self):
34+
body_json = {'model': 'testing-model',
35+
'modelPath': '/path/to/local/gguf',
36+
'name': 'test_model',
37+
'option': 'copy'}
38+
response = requests.post("http://localhost:3928/models/import", json=body_json)
39+
assert response.status_code == 200
40+
# Test imported path
41+
response = requests.get("http://localhost:3928/models/testing-model")
42+
assert response.status_code == 200
43+
# Since this is a dynamic test - require actual file path
44+
# it's not safe to assert with the gguf file name
45+
assert response.json()['files'][0] != '/path/to/local/gguf'
46+
3247
def test_model_import_with_invalid_path_should_fail(self):
3348
body_json = {'model': 'tinyllama:gguf',
3449
'modelPath': '/invalid/path/to/gguf'}

engine/services/model_service.cc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
namespace {
1919
void ParseGguf(const DownloadItem& ggufDownloadItem,
2020
std::optional<std::string> author,
21-
std::optional<std::string> name) {
21+
std::optional<std::string> name,
22+
std::optional<std::uint64_t> size) {
2223
namespace fs = std::filesystem;
2324
namespace fmu = file_manager_utils;
2425
config::GGUFHandler gguf_handler;
@@ -35,6 +36,7 @@ void ParseGguf(const DownloadItem& ggufDownloadItem,
3536
model_config.model = ggufDownloadItem.id;
3637
model_config.name =
3738
name.has_value() ? name.value() : gguf_handler.GetModelConfig().name;
39+
model_config.size = size.value_or(0);
3840
yaml_handler.UpdateModelConfig(model_config);
3941

4042
auto yaml_path{ggufDownloadItem.localPath};
@@ -284,8 +286,13 @@ cpp::result<DownloadTask, std::string> ModelService::HandleDownloadUrlAsync(
284286
}}}};
285287

286288
auto on_finished = [author, temp_name](const DownloadTask& finishedTask) {
289+
// Sum downloadedBytes from all items
290+
uint64_t model_size = 0;
291+
for (const auto& item : finishedTask.items) {
292+
model_size = model_size + item.bytes.value_or(0);
293+
}
287294
auto gguf_download_item = finishedTask.items[0];
288-
ParseGguf(gguf_download_item, author, temp_name);
295+
ParseGguf(gguf_download_item, author, temp_name, model_size);
289296
};
290297

291298
downloadTask.id = unique_model_id;
@@ -349,8 +356,13 @@ cpp::result<std::string, std::string> ModelService::HandleUrl(
349356
}}}};
350357

351358
auto on_finished = [author](const DownloadTask& finishedTask) {
359+
// Sum downloadedBytes from all items
360+
uint64_t model_size = 0;
361+
for (const auto& item : finishedTask.items) {
362+
model_size = model_size + item.bytes.value_or(0);
363+
}
352364
auto gguf_download_item = finishedTask.items[0];
353-
ParseGguf(gguf_download_item, author, std::nullopt);
365+
ParseGguf(gguf_download_item, author, std::nullopt, model_size);
354366
};
355367

356368
auto result = download_service_->AddDownloadTask(downloadTask, on_finished);

0 commit comments

Comments
 (0)