Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/static/openapi/cortex.json
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,46 @@
"tags": ["Models"]
}
},
"/v1/models/import": {
"post": {
"operationId": "ModelsController_importModel",
"summary": "Import model",
"description": "Imports a model from a specified path.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ImportModelRequest"
},
"example": {
"model": "model-id",
"modelPath": "/path/to/gguf",
"name": "model display name"
}
}
}
},
"responses": {
"200": {
"description": "Model is imported successfully!",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ImportModelResponse"
},
"example": {
"message": "Model is imported successfully!",
"modelHandle": "model-id",
"result": "OK"
}
}
}
}
},
"tags": ["Models"]
}
},
"/v1/threads": {
"post": {
"operationId": "ThreadsController_create",
Expand Down Expand Up @@ -1803,6 +1843,43 @@
}
}
},
"ImportModelRequest": {
"type": "object",
"properties": {
"model": {
"type": "string",
"description": "The unique identifier of the model."
},
"modelPath": {
"type": "string",
"description": "The file path to the model."
},
"name": {
"type": "string",
"description": "The display name of the model."
}
},
"required": ["model", "modelPath"]
},
"ImportModelResponse": {
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "Success message."
},
"modelHandle": {
"type": "string",
"description": "The unique identifier of the imported model."
},
"result": {
"type": "string",
"description": "Result status.",
"example": "OK"
}
},
"required": ["message", "modelHandle", "result"]
},
"CommonResponseDto": {
"type": "object",
"properties": {
Expand Down
2 changes: 2 additions & 0 deletions engine/controllers/models.cc
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ void Models::ImportModel(
}
auto modelHandle = (*(req->getJsonObject())).get("model", "").asString();
auto modelPath = (*(req->getJsonObject())).get("modelPath", "").asString();
auto modelName = (*(req->getJsonObject())).get("name", "").asString();
config::GGUFHandler gguf_handler;
config::YamlHandler yaml_handler;
cortex::db::Models modellist_utils_obj;
Expand All @@ -333,6 +334,7 @@ void Models::ImportModel(
config::ModelConfig model_config = gguf_handler.GetModelConfig();
model_config.files.push_back(modelPath);
model_config.model = modelHandle;
model_config.name = modelName.empty() ? model_config.name : modelName;
yaml_handler.UpdateModelConfig(model_config);

if (modellist_utils_obj.AddModelEntry(model_entry).value()) {
Expand Down
24 changes: 22 additions & 2 deletions engine/e2e-test/test_api_model_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,25 @@ def setup_and_teardown(self):
def test_model_import_should_be_success(self):
body_json = {'model': 'tinyllama:gguf',
'modelPath': '/path/to/local/gguf'}
response = requests.post("http://localhost:3928/models/import", json = body_json)
assert response.status_code == 200
response = requests.post("http://localhost:3928/models/import", json=body_json)
assert response.status_code == 200

@pytest.mark.skipif(True, reason="Expensive test. Only test when you have local gguf file.")
def test_model_import_with_name_should_be_success(self):
body_json = {'model': 'tinyllama:gguf',
'modelPath': '/path/to/local/gguf',
'name': 'test_model'}
response = requests.post("http://localhost:3928/models/import", json=body_json)
assert response.status_code == 200

def test_model_import_with_invalid_path_should_fail(self):
body_json = {'model': 'tinyllama:gguf',
'modelPath': '/invalid/path/to/gguf'}
response = requests.post("http://localhost:3928/models/import", json=body_json)
assert response.status_code == 400

def test_model_import_with_missing_model_should_fail(self):
body_json = {'modelPath': '/path/to/local/gguf'}
response = requests.post("http://localhost:3928/models/import", json=body_json)
print(response)
assert response.status_code == 409
Loading