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

Commit 55bbe0d

Browse files
authored
chore: add model import option parameter (#1627)
* chore: add model import option parameter * chore: update openai doc schema to enum * chore: change back skipped test case * fix: C2664 - push_back path
1 parent 98a0438 commit 55bbe0d

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

docs/static/openapi/cortex.json

Lines changed: 7 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"]

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'}

0 commit comments

Comments
 (0)