-
Notifications
You must be signed in to change notification settings - Fork 261
default task detection based on model config #4317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
981bdf6
ba4eac9
d3de201
98ea635
fac11b3
f6fa1d9
29292b4
055352d
5673c47
60984c7
8520129
fe7ef4b
ca3d558
faa924a
72d016b
954f297
97304c9
29d940f
d292e9c
acf6eb9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,14 +16,18 @@ | |
| #include "cli_parser.hpp" | ||
|
|
||
| #include <filesystem> | ||
| #include <fstream> | ||
| #include <iostream> | ||
| #include <optional> | ||
| #include <stdexcept> | ||
| #include <string> | ||
| #include <utility> | ||
| #include <vector> | ||
| #include <variant> | ||
|
|
||
| #include "capi_frontend/server_settings.hpp" | ||
| #include "default_task.hpp" | ||
| #include "logging.hpp" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spdlog was not used in cli_parser on purpose - during cli parsing spdlog could be not initialized yet. |
||
| #include "graph_export/graph_cli_parser.hpp" | ||
| #include "graph_export/rerank_graph_cli_parser.hpp" | ||
| #include "graph_export/embeddings_graph_cli_parser.hpp" | ||
|
|
@@ -33,13 +37,14 @@ | |
| #include "ovms_exit_codes.hpp" | ||
| #include "filesystem/filesystem.hpp" | ||
| #include "filesystem/localfilesystem.hpp" | ||
| #include "stringutils.hpp" | ||
| #include "version.hpp" | ||
|
|
||
| namespace ovms { | ||
|
|
||
| constexpr const char* CONFIG_MANAGEMENT_HELP_GROUP{"config management"}; | ||
| constexpr const char* API_KEY_ENV_VAR{"API_KEY"}; | ||
| constexpr const char* MODEL_CONFIG_FILENAME{"config.json"}; | ||
| constexpr const char* MODEL_INDEX_FILENAME{"model_index.json"}; | ||
|
|
||
| std::string getConfigPath(const std::string& configPath) { | ||
| bool isDir = false; | ||
|
|
@@ -53,6 +58,19 @@ std::string getConfigPath(const std::string& configPath) { | |
| return configPath; | ||
| } | ||
|
|
||
| std::string CLIParser::getEffectiveTaskParameter() const { | ||
| if (result->count("task")) { | ||
| const auto task = result->operator[]("task").as<std::string>(); | ||
| SPDLOG_DEBUG("Effective task parameter specified by user: {}", task); | ||
| return task; | ||
| } | ||
| if (inferredTaskParameter.has_value()) { | ||
| SPDLOG_DEBUG("Effective task parameter using inferred default: {}", inferredTaskParameter.value()); | ||
| return inferredTaskParameter.value(); | ||
| } | ||
| throw std::logic_error("error parsing options - --task parameter wasn't passed"); | ||
| } | ||
|
|
||
| std::variant<bool, std::pair<int, std::string>> CLIParser::parse(int argc, char** argv) { | ||
| std::stringstream ss; | ||
| try { | ||
|
|
@@ -299,7 +317,7 @@ std::variant<bool, std::pair<int, std::string>> CLIParser::parse(int argc, char* | |
|
|
||
| options->add_options("generative task (applies to: pull hf model, single model)") | ||
| ("task", | ||
| "Specifies the generative task for the local model. It should be followed by task specific parameters. Supported tasks: text_generation, embeddings, rerank, image_generation, text2speech, speech2text. It creates the pipeline graph in memory based on the provided task-specific options.", | ||
| "Specifies the generative task for the local model. If not provided, default task value is inferred from model config.json architectures. It should be followed by task specific parameters. Supported tasks: text_generation, embeddings, rerank, image_generation, text2speech, speech2text. It creates the pipeline graph in memory based on the provided task-specific options.", | ||
| cxxopts::value<std::string>(), | ||
| "TASK"); | ||
| configOptions->custom_help(""); | ||
|
|
@@ -335,12 +353,73 @@ std::variant<bool, std::pair<int, std::string>> CLIParser::parse(int argc, char* | |
|
|
||
| result = std::make_unique<cxxopts::ParseResult>(options->parse(argc, argv)); | ||
|
|
||
| const bool isConfigManagementFlow = | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could be a CLIParser method |
||
| result->count("add_to_config") || result->count("remove_from_config") || result->count("list_models"); | ||
| if (!result->count("task") && | ||
| !result->count("pull") && | ||
| !result->count("source_model") && | ||
| result->count("model_path") && | ||
| !isConfigManagementFlow && | ||
| !result->count("help") && | ||
| !result->count("version")) { | ||
| const std::optional<std::string> modelPath = std::make_optional(result->operator[]("model_path").as<std::string>()); | ||
| const auto configPath = std::filesystem::path(*modelPath) / MODEL_CONFIG_FILENAME; | ||
| const auto indexPath = std::filesystem::path(*modelPath) / MODEL_INDEX_FILENAME; | ||
| if (std::filesystem::exists(configPath) || std::filesystem::exists(indexPath)) { | ||
| // Check if task-specific parameters are provided or if graph.pbtxt is missing | ||
| bool hasUnmatchedOptions = ::ovms::hasTaskSpecificParameters(result->unmatched()); | ||
| bool graphExists = ::ovms::graphPbtxtExists(*modelPath); | ||
|
|
||
| // Infer task if: | ||
| // 1. Task-specific parameters are provided (unmatched options), OR | ||
| // 2. graph.pbtxt doesn't exist (need to create in-memory graph) | ||
| // Otherwise, if graph.pbtxt exists and no task parameters, use the filesystem graph | ||
| if (hasUnmatchedOptions || !graphExists) { | ||
| try { | ||
| inferredTaskParameter = determineDefaultTaskParameter(modelPath, std::nullopt, std::nullopt); | ||
| } catch (const std::exception& e) { | ||
| SPDLOG_DEBUG("Default task inference skipped for model_path '{}': {}", modelPath.value_or(""), e.what()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // HF pull mode or pull and start mode or starting from local folder with graph created in memory | ||
| if (isHFPullOrPullAndStart(this->result) || isInMemoryGraphMode(this->result)) { | ||
| std::vector<std::string> unmatchedOptions; | ||
| GraphExportType task; | ||
| if (result->count("task")) { | ||
| task = stringToEnum(result->operator[]("task").as<std::string>()); | ||
| std::string taskValue; | ||
| if (!result->count("task") && !result->count("help") && !result->count("version")) { | ||
| const std::optional<std::string> modelPath = result->count("model_path") ? std::make_optional(result->operator[]("model_path").as<std::string>()) : std::nullopt; | ||
| const std::optional<std::string> sourceModel = result->count("source_model") ? std::make_optional(result->operator[]("source_model").as<std::string>()) : std::nullopt; | ||
|
dtrawins marked this conversation as resolved.
|
||
| const std::optional<std::string> modelRepositoryPath = result->count("model_repository_path") ? std::make_optional(result->operator[]("model_repository_path").as<std::string>()) : std::nullopt; | ||
|
|
||
| // For source_model (HF pull mode), always infer the task | ||
| // For model_path in in-memory graph mode, check if task should be inferred based on parameters and graph.pbtxt | ||
| bool shouldInferTask = false; | ||
| if (sourceModel.has_value() && !sourceModel->empty()) { | ||
| // Always infer task when pulling from HuggingFace | ||
| shouldInferTask = true; | ||
| } else if (modelPath.has_value() && !modelPath->empty()) { | ||
| // For local model_path, infer task if: | ||
| // 1. Unmatched options (task-specific parameters) are present, OR | ||
| // 2. graph.pbtxt doesn't exist (need to create in-memory graph) | ||
| bool hasUnmatchedOptions = ::ovms::hasTaskSpecificParameters(result->unmatched()); | ||
| bool graphExists = ::ovms::graphPbtxtExists(*modelPath); | ||
| shouldInferTask = hasUnmatchedOptions || !graphExists; | ||
| } | ||
|
|
||
| if (shouldInferTask) { | ||
| try { | ||
| inferredTaskParameter = determineDefaultTaskParameter(modelPath, sourceModel, modelRepositoryPath); | ||
| } catch (const std::exception& e) { | ||
| SPDLOG_DEBUG("Default task inference skipped for source_model '{}': {}", sourceModel.value_or(""), e.what()); | ||
| } | ||
| } | ||
| } | ||
| taskValue = getEffectiveTaskParameter(); | ||
| task = stringToEnum(taskValue); | ||
| if (task != UNKNOWN_GRAPH) { | ||
| switch (task) { | ||
| case TEXT_GENERATION_GRAPH: { | ||
| GraphCLIParser cliParser; | ||
|
|
@@ -379,12 +458,12 @@ std::variant<bool, std::pair<int, std::string>> CLIParser::parse(int argc, char* | |
| break; | ||
| } | ||
| case UNKNOWN_GRAPH: { | ||
| ss << "error parsing options - --task parameter unsupported value: " + result->operator[]("task").as<std::string>(); | ||
| ss << "error parsing options - --task parameter unsupported value: " + taskValue; | ||
| return std::make_pair(OVMS_EX_USAGE, ss.str()); | ||
| } | ||
| } | ||
| } else { | ||
| ss << "error parsing options - --task parameter wasn't passed"; | ||
| ss << "error parsing options - --task parameter unsupported value: " + taskValue; | ||
| return std::make_pair(OVMS_EX_USAGE, ss.str()); | ||
| } | ||
|
|
||
|
|
@@ -671,11 +750,14 @@ bool CLIParser::isHFPullOrPullAndStart(const std::unique_ptr<cxxopts::ParseResul | |
| // parse-time checks that rely on this helper continue to reject combining | ||
| // task-based flows with config-management modes. More specific mode | ||
| // differentiation is handled by isInMemoryGraphMode(). | ||
| return (result->count("pull") || result->count("task")); | ||
| return (result->count("pull") || result->count("task") || result->count("source_model")); | ||
| } | ||
|
|
||
| bool CLIParser::isInMemoryGraphMode(const std::unique_ptr<cxxopts::ParseResult>& result) { | ||
| return (result->count("task") && !result->count("source_model") && !result->count("pull")); | ||
| if (result->count("source_model") || result->count("pull")) { | ||
| return false; | ||
| } | ||
| return result->count("task") || inferredTaskParameter.has_value(); | ||
| } | ||
|
|
||
| void CLIParser::prepareGraph(ServerSettingsImpl& serverSettings, HFSettingsImpl& hfSettings, const std::string& modelName) { | ||
|
|
@@ -731,10 +813,16 @@ void CLIParser::prepareGraph(ServerSettingsImpl& serverSettings, HFSettingsImpl& | |
| // When --task is used with --model_path but without --pull/--source_model, | ||
| // use model_path as the model location (no HF download needed) | ||
| if (!result->count("pull") && !result->count("source_model") && result->count("model_path")) { | ||
| hfSettings.exportSettings.modelPath = result->operator[]("model_path").as<std::string>(); | ||
| const auto configuredModelPath = std::filesystem::path(result->operator[]("model_path").as<std::string>()); | ||
| hfSettings.exportSettings.modelPath = std::filesystem::absolute(configuredModelPath).lexically_normal().string(); | ||
| SPDLOG_DEBUG("Using local absolute model path for graph export: {}", hfSettings.exportSettings.modelPath); | ||
| } | ||
| const std::string taskValue = getEffectiveTaskParameter(); | ||
| if (inferredTaskParameter.has_value()) { | ||
| SPDLOG_INFO("Identified default task '{}' from model config", inferredTaskParameter.value()); | ||
|
Comment on lines
+818
to
+822
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We did not use spdlog in cli_parser on purpose. Logging could not be setup yet. Use cout. We also did use cout for model pulling. |
||
| } | ||
| if (result->count("task")) { | ||
| hfSettings.task = stringToEnum(result->operator[]("task").as<std::string>()); | ||
| if (!taskValue.empty()) { | ||
| hfSettings.task = stringToEnum(taskValue); | ||
| switch (hfSettings.task) { | ||
| case TEXT_GENERATION_GRAPH: { | ||
| if (std::holds_alternative<GraphCLIParser>(this->graphOptionsParser)) { | ||
|
|
@@ -785,7 +873,7 @@ void CLIParser::prepareGraph(ServerSettingsImpl& serverSettings, HFSettingsImpl& | |
| break; | ||
| } | ||
| case UNKNOWN_GRAPH: { | ||
| throw std::logic_error("Error: --task parameter unsupported value: " + result->operator[]("task").as<std::string>()); | ||
| throw std::logic_error("Error: --task parameter unsupported value: " + taskValue); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add as separate test library target and make those new config files as data field there.