Skip to content

Commit 6aa801d

Browse files
authored
list servables in repository (#3287)
Ticket:CVS-163279
1 parent 10d434d commit 6aa801d

17 files changed

+841
-3
lines changed

src/BUILD

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,11 +416,21 @@ cc_library(
416416
copts = COPTS_ADJUSTED,
417417
linkopts = LINKOPTS_ADJUSTED,
418418
)
419+
cc_library(
420+
name = "modelextensions",
421+
hdrs = ["modelextensions.hpp",],
422+
srcs = ["modelextensions.cpp",],
423+
visibility = ["//visibility:public",],
424+
local_defines = COMMON_LOCAL_DEFINES,
425+
copts = COPTS_ADJUSTED,
426+
linkopts = LINKOPTS_ADJUSTED,
427+
)
419428
cc_library(
420429
name = "modelinstance_h",
421430
hdrs = ["modelinstance.hpp",],
422431
srcs = [],
423432
deps = [
433+
"modelextensions",
424434
"libovmslogging",
425435
"libovms_outputkeeper",
426436
"libovmsprofiler",
@@ -701,6 +711,7 @@ cc_library(
701711
"//src/kfserving_api:kfserving_api_cpp",
702712
"capimodule",
703713
"//src/pull_module:hf_pull_model_module",
714+
"//src/servables_config_manager_module:servablesconfigmanagermodule",
704715
"predict_request_validation_utils", # to be removed when capi has its own lib and added there @atobisze
705716
"kfs_backend_impl",
706717
"tfs_backend_impl",
@@ -2966,6 +2977,7 @@ cc_test(
29662977
"//src:custom_nodes_common_buffersqueue",
29672978
"@com_google_googletest//:gtest",
29682979
":pull_hf_model_test",
2980+
":listmodels_test",
29692981
":graph_export_test",
29702982
] + select({
29712983
"//conditions:default": [
@@ -3075,6 +3087,7 @@ cc_library(
30753087
srcs = ["test/pull_hf_model_test.cpp"],
30763088
linkopts = [],
30773089
deps = [
3090+
"//src/servables_config_manager_module:listmodels",
30783091
":test_utils",
30793092
"//src/pull_module:libgit2",
30803093
"//src/pull_module:hf_pull_model_module",
@@ -3085,6 +3098,21 @@ cc_library(
30853098
local_defines = COMMON_LOCAL_DEFINES,
30863099
copts = COPTS_TESTS,
30873100
)
3101+
cc_library(
3102+
name = "listmodels_test",
3103+
srcs = ["test/listmodels_test.cpp"],
3104+
alwayslink = True,
3105+
linkopts = [],
3106+
deps = [
3107+
"//src/servables_config_manager_module:listmodels",
3108+
":test_utils",
3109+
#"//src:ovms_lib",
3110+
"libovmsstring_utils",
3111+
"@com_google_googletest//:gtest",
3112+
],
3113+
local_defines = COMMON_LOCAL_DEFINES,
3114+
copts = COPTS_TESTS,
3115+
)
30883116
cc_library(
30893117
name = "graph_export_test",
30903118
linkstatic = 1,

src/capi_frontend/server_settings.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct ServerSettingsImpl {
7373
std::string cacheDir;
7474
bool withPython = false;
7575
bool startedWithCLI = false;
76+
bool listServables = false;
7677
HFSettingsImpl hfSettings;
7778
};
7879

src/cli_parser.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,11 @@ void CLIParser::parse(int argc, char** argv) {
136136
("model_repository_path",
137137
"HF model destination download path",
138138
cxxopts::value<std::string>(),
139-
"MODEL_REPOSITORY_PATH");
139+
"MODEL_REPOSITORY_PATH")
140+
("list_models",
141+
"Directive to show available servables in models repository",
142+
cxxopts::value<bool>()->default_value("false"),
143+
"LIST_MODELS");
140144

141145
options->add_options("single model")
142146
("model_name",
@@ -224,7 +228,13 @@ void CLIParser::prepare(ServerSettingsImpl* serverSettings, ModelsSettingsImpl*
224228
if (nullptr == result) {
225229
throw std::logic_error("Tried to prepare server and model settings without parse result");
226230
}
227-
231+
// list models mode
232+
if (result->count("list_models")) {
233+
serverSettings->listServables = result->operator[]("list_models").as<bool>();
234+
if (result->count("model_repository_path"))
235+
serverSettings->hfSettings.downloadPath = result->operator[]("model_repository_path").as<std::string>();
236+
return;
237+
}
228238
// Ovms Pull models mode
229239
if (result->count("pull")) {
230240
serverSettings->hfSettings.pullHfModelMode = result->operator[]("pull").as<bool>();

src/config.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ bool Config::validate() {
8888
if (this->serverSettings.hfSettings.pullHfModelMode) {
8989
return true;
9090
}
91+
if (this->serverSettings.listServables) {
92+
if (this->serverSettings.hfSettings.downloadPath.empty()) {
93+
std::cerr << "Use --list_models with --model_repository_path" << std::endl;
94+
return false;
95+
}
96+
return true;
97+
}
98+
9199
if (!configPath().empty() && (!modelName().empty() || !modelPath().empty())) {
92100
std::cerr << "Use either config_path or model_path with model_name" << std::endl;
93101
return false;

src/modelextensions.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//*****************************************************************************
2+
// Copyright 2025 Intel Corporation
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//*****************************************************************************
16+
#include "modelextensions.hpp"
17+
18+
namespace ovms {
19+
const std::array<const char*, 2> OV_MODEL_FILES_EXTENSIONS{".xml", ".bin"};
20+
const std::array<const char*, 1> ONNX_MODEL_FILES_EXTENSIONS{".onnx"};
21+
const std::array<const char*, 2> PADDLE_MODEL_FILES_EXTENSIONS{".pdmodel", ".pdiparams"};
22+
const std::array<const char*, 1> TF_MODEL_FILES_EXTENSIONS{".pb"};
23+
const std::array<const char*, 1> TFLITE_MODEL_FILES_EXTENSIONS{".tflite"};
24+
} // namespace ovms

src/modelextensions.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//*****************************************************************************
2+
// Copyright 2025 Intel Corporation
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//*****************************************************************************
16+
#pragma once
17+
#include <array>
18+
19+
namespace ovms {
20+
extern const std::array<const char*, 2> OV_MODEL_FILES_EXTENSIONS;
21+
extern const std::array<const char*, 1> ONNX_MODEL_FILES_EXTENSIONS;
22+
extern const std::array<const char*, 2> PADDLE_MODEL_FILES_EXTENSIONS;
23+
extern const std::array<const char*, 1> TF_MODEL_FILES_EXTENSIONS;
24+
extern const std::array<const char*, 1> TFLITE_MODEL_FILES_EXTENSIONS;
25+
} // namespace ovms

src/module_names.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ const std::string HF_MODEL_PULL_MODULE_NAME = "HfModelPullModule";
2424
const std::string METRICS_MODULE_NAME = "MetricsModule";
2525
const std::string PYTHON_INTERPRETER_MODULE_NAME = "PythonInterpreterModule";
2626
const std::string CAPI_MODULE_NAME = "C-APIModule";
27+
const std::string SERVABLES_CONFIG_MANAGER_MODULE_NAME = {"ServablesConfigManagerModule"};
2728
} // namespace ovms

src/module_names.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ extern const std::string HF_MODEL_PULL_MODULE_NAME;
2525
extern const std::string METRICS_MODULE_NAME;
2626
extern const std::string PYTHON_INTERPRETER_MODULE_NAME;
2727
extern const std::string CAPI_MODULE_NAME;
28+
extern const std::string SERVABLES_CONFIG_MANAGER_MODULE_NAME;
2829
} // namespace ovms
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#
2+
# Copyright (c) 2025 Intel Corporation
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
load("//:common_settings.bzl",
18+
"COMMON_STATIC_LIBS_COPTS", "COMMON_STATIC_LIBS_LINKOPTS", "COMMON_FUZZER_COPTS", "COMMON_FUZZER_LINKOPTS", "COMMON_LOCAL_DEFINES", "PYBIND_DEPS")
19+
20+
COPTS_ADJUSTED = COMMON_STATIC_LIBS_COPTS + select({
21+
"//conditions:default": [],
22+
"//:fuzzer_build" : COMMON_FUZZER_COPTS,
23+
})
24+
25+
LINKOPTS_ADJUSTED = COMMON_STATIC_LIBS_LINKOPTS + select({
26+
"//conditions:default": [],
27+
"//:fuzzer_build" : COMMON_FUZZER_LINKOPTS,
28+
})
29+
30+
cc_library(
31+
name = "listmodels",
32+
hdrs = ["listmodels.hpp",],
33+
srcs = ["listmodels.cpp",],
34+
deps = [
35+
"//src:libovmsfilesystem",
36+
"//src:libovmslogging",
37+
"//src:modelextensions",
38+
],
39+
visibility = ["//visibility:public",],
40+
local_defines = COMMON_LOCAL_DEFINES,
41+
copts = COPTS_ADJUSTED,
42+
linkopts = LINKOPTS_ADJUSTED,
43+
)
44+
cc_library(
45+
name = "servablesconfigmanagermodule",
46+
srcs = ["servablesconfigmanagermodule.cpp"],
47+
hdrs = ["servablesconfigmanagermodule.hpp"],
48+
deps = [
49+
"listmodels",
50+
"//src:cpp_headers",
51+
"//src:libovmsstring_utils",
52+
"//src:libovmslogging",
53+
"//src:libovms_server_settings",
54+
"//src:libovms_module",
55+
],
56+
visibility = ["//visibility:public"],
57+
copts = COPTS_ADJUSTED,
58+
linkopts = LINKOPTS_ADJUSTED,
59+
local_defines = COMMON_LOCAL_DEFINES,
60+
)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//*****************************************************************************
2+
// Copyright 2025 Intel Corporation
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//*****************************************************************************
16+
#include "listmodels.hpp"
17+
18+
#include <filesystem>
19+
#include <fstream>
20+
#include <iostream>
21+
22+
#include "src/filesystem.hpp"
23+
#include "src/modelextensions.hpp"
24+
#include "src/logging.hpp"
25+
26+
namespace ovms {
27+
28+
bool isMediapipeGraphDir(const std::string& path) {
29+
std::filesystem::path graphPath(path);
30+
graphPath /= "graph.pbtxt";
31+
return std::filesystem::exists(graphPath) && std::filesystem::is_regular_file(graphPath);
32+
}
33+
bool isVersionDir(const std::string& path) {
34+
std::string dirName = std::filesystem::path(path).filename().string();
35+
return !dirName.empty() &&
36+
std::all_of(dirName.begin(), dirName.end(), ::isdigit) &&
37+
dirName[0] != '0' &&
38+
std::filesystem::is_directory(path);
39+
}
40+
41+
std::string getPartialPath(const std::filesystem::path& path, int depth) {
42+
std::string partialPath = path.filename().string();
43+
std::filesystem::path parentPath = path.parent_path();
44+
for (int i = 0; i < depth; ++i) {
45+
if (parentPath.empty() || parentPath == parentPath.root_path()) {
46+
SPDLOG_ERROR("Error trying to get partial path:{}, {}", partialPath, i);
47+
throw std::runtime_error("Depth is greater than the number of directories");
48+
}
49+
partialPath = ovms::FileSystem::appendSlash(parentPath.filename().string()) + partialPath;
50+
parentPath = parentPath.parent_path();
51+
}
52+
SPDLOG_TRACE("Current partial path:{}", partialPath);
53+
return partialPath;
54+
}
55+
static bool registeredModelFromThisDirectory(const std::filesystem::path& path, std::unordered_map<std::string, ServableType_t>& servablesList, int depth) {
56+
const std::string partialPath(getPartialPath(path, depth));
57+
for (const auto& entry : std::filesystem::directory_iterator(path)) {
58+
if (!isVersionDir(entry.path().string())) {
59+
continue;
60+
}
61+
SPDLOG_TRACE("Entry is a version directory: {}", entry.path().string());
62+
// i want to have filename + up to 3 levels of directories
63+
if (hasRequiredExtensions(entry.path().string(), OV_MODEL_FILES_EXTENSIONS)) {
64+
servablesList[partialPath] = SERVABLE_TYPE_MODEL;
65+
return true;
66+
} else if (hasRequiredExtensions(entry.path().string(), ONNX_MODEL_FILES_EXTENSIONS)) {
67+
servablesList[partialPath] = SERVABLE_TYPE_MODEL;
68+
return true;
69+
} else if (hasRequiredExtensions(entry.path().string(), PADDLE_MODEL_FILES_EXTENSIONS)) {
70+
servablesList[partialPath] = SERVABLE_TYPE_MODEL;
71+
return true;
72+
} else if (hasRequiredExtensions(entry.path().string(), TF_MODEL_FILES_EXTENSIONS)) {
73+
servablesList[partialPath] = SERVABLE_TYPE_MODEL;
74+
return true;
75+
} else if (hasRequiredExtensions(entry.path().string(), TFLITE_MODEL_FILES_EXTENSIONS)) {
76+
servablesList[partialPath] = SERVABLE_TYPE_MODEL;
77+
return true;
78+
}
79+
}
80+
return false;
81+
}
82+
83+
static bool registeredGraphFromThisDirectory(const std::filesystem::path& path, std::unordered_map<std::string, ServableType_t>& servablesList, int depth) {
84+
if (isMediapipeGraphDir(path.string())) {
85+
SPDLOG_TRACE("Found mediapipe graph: {}", path.string());
86+
servablesList[getPartialPath(path, depth)] = SERVABLE_TYPE_MEDIAPIPEGRAPH;
87+
return true;
88+
}
89+
return false;
90+
}
91+
92+
static void listServables(const std::filesystem::path& directoryPath, std::unordered_map<std::string, ServableType_t>& servablesList, int depth) {
93+
SPDLOG_TRACE("Listing servables in directory: {}", directoryPath.string());
94+
if (!std::filesystem::is_directory(directoryPath)) {
95+
SPDLOG_TRACE("Path is not a directory: {}", directoryPath.string());
96+
return;
97+
}
98+
if (std::filesystem::is_empty(directoryPath)) {
99+
SPDLOG_TRACE("Directory is empty: {}", directoryPath.string());
100+
return;
101+
}
102+
SPDLOG_TRACE("Directory name: {}", directoryPath.filename().string());
103+
if (registeredGraphFromThisDirectory(directoryPath.string(), servablesList, depth)) {
104+
return;
105+
}
106+
if (registeredModelFromThisDirectory(directoryPath.string(), servablesList, depth)) {
107+
return;
108+
}
109+
for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) {
110+
if (entry.is_directory()) {
111+
listServables(entry.path(), servablesList, depth + 1);
112+
}
113+
}
114+
SPDLOG_TRACE("No servables found in directory: {}", directoryPath.string());
115+
}
116+
std::unordered_map<std::string, ServableType_t> listServables(const std::string directoryPath) {
117+
SPDLOG_TRACE("Listing servables in directory: {}", directoryPath);
118+
std::unordered_map<std::string, ServableType_t> servablesList;
119+
std::filesystem::path path(directoryPath);
120+
if (!std::filesystem::is_directory(path)) {
121+
SPDLOG_ERROR("Path is not a directory: {}", directoryPath);
122+
return servablesList;
123+
}
124+
if (std::filesystem::is_empty(path)) {
125+
SPDLOG_ERROR("Directory is empty: {}", directoryPath);
126+
return servablesList;
127+
}
128+
SPDLOG_TRACE("Directory name: {}", path.filename().string());
129+
for (const auto& file : std::filesystem::directory_iterator(path)) {
130+
listServables(file.path(), servablesList, 0);
131+
}
132+
return servablesList;
133+
}
134+
} // namespace ovms

0 commit comments

Comments
 (0)