diff --git a/.github/references/ubuntu_22_04_clang_arm_manifest.json b/.github/references/ubuntu_22_04_clang_arm_manifest.json index bcb495d3e9..750f3954b7 100644 --- a/.github/references/ubuntu_22_04_clang_arm_manifest.json +++ b/.github/references/ubuntu_22_04_clang_arm_manifest.json @@ -10268,7 +10268,7 @@ "Min P": { "name": "Min P", "description": "Sets a minimum base probability threshold for token selection. 0.0 = disabled.", - "validator": "VALID", + "validator": "NUMBER_VALIDATOR", "required": "false", "sensitive": "false", "expressionLanguageScope": "NONE" diff --git a/core-framework/common/include/utils/ParsingUtils.h b/core-framework/common/include/utils/ParsingUtils.h index 33236eda8a..85e18bde7b 100644 --- a/core-framework/common/include/utils/ParsingUtils.h +++ b/core-framework/common/include/utils/ParsingUtils.h @@ -40,7 +40,8 @@ std::expected parseDataSize(std::string_view input); std::expected parseUnixOctalPermissions(std::string_view input); -std::expected parseFloat(std::string_view input); +template +std::expected parseFloatingPoint(std::string_view input); template std::expected parseIntegralMinMax(const std::string_view input, const T minimum, const T maximum) { @@ -90,4 +91,22 @@ std::expected parseEnum(const std::string_view input) { return *result; } +template +std::expected parseFloatingPoint(const std::string_view input) { + const auto trimmed_input = utils::string::trim(input); + T value{}; + + const auto [ptr, ec] = std::from_chars(trimmed_input.data(), trimmed_input.data() + trimmed_input.size(), value); + + if (ec != std::errc()) { + return std::unexpected{core::ParsingErrorCode::GeneralParsingError}; + } + + if (ptr != trimmed_input.data() + trimmed_input.size()) { + return std::unexpected{core::ParsingErrorCode::GeneralParsingError}; + } + + return value; +} + } // namespace org::apache::nifi::minifi::parsing diff --git a/core-framework/common/src/utils/ParsingUtils.cpp b/core-framework/common/src/utils/ParsingUtils.cpp index 922704b950..75f095ef20 100644 --- a/core-framework/common/src/utils/ParsingUtils.cpp +++ b/core-framework/common/src/utils/ParsingUtils.cpp @@ -126,12 +126,4 @@ std::expected parseUnixOctalPermissions(const std::st return result; } -std::expected parseFloat(std::string_view input) { - try { - return std::stof(std::string{input}); - } catch(const std::exception&) { - return std::unexpected{core::ParsingErrorCode::GeneralParsingError}; - } -} - } // namespace org::apache::nifi::minifi::parsing diff --git a/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h b/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h index 2ec9571edd..ea2c17d9ed 100644 --- a/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h +++ b/extension-framework/cpp-extension-lib/include/api/utils/ProcessorConfigUtils.h @@ -134,7 +134,7 @@ inline std::optional parseOptionalFloatProperty(const core::ProcessContex if (property_str->empty()) { return std::nullopt; } - return parsing::parseFloat(*property_str) + return parsing::parseFloatingPoint(*property_str) | minifi::utils::orThrow(fmt::format("Expected parsable float from \"{}\"", property.name)); } return std::nullopt; diff --git a/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp b/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp index e11fd8972d..cf70d2c718 100644 --- a/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp +++ b/extension-framework/cpp-extension-lib/src/utils/minifi-c-utils.cpp @@ -55,6 +55,9 @@ minifi_validator toStandardPropertyValidator(const minifi::core::PropertyValidat if (validator->getEquivalentNifiStandardValidatorName() == minifi::core::StandardPropertyValidators::PORT_VALIDATOR.getEquivalentNifiStandardValidatorName()) { return MINIFI_VALIDATOR_PORT; } + if (validator->getEquivalentNifiStandardValidatorName() == minifi::core::StandardPropertyValidators::NUMBER_VALIDATOR.getEquivalentNifiStandardValidatorName()) { + return MINIFI_VALIDATOR_NUMBER; + } gsl_FailFast(); } diff --git a/extension-framework/include/utils/ProcessorConfigUtils.h b/extension-framework/include/utils/ProcessorConfigUtils.h index b69ed40d97..b3b5071e55 100644 --- a/extension-framework/include/utils/ProcessorConfigUtils.h +++ b/extension-framework/include/utils/ProcessorConfigUtils.h @@ -55,6 +55,18 @@ inline int64_t parseI64Property(const core::ProcessContext& ctx, const core::Pro | orThrow(fmt::format("Expected parsable int64_t from \"{}\"", property.name)); } +inline double parseF64Property(const core::ProcessContext& ctx, const core::PropertyReference& property, const core::FlowFile* flow_file = nullptr) { + return ctx.getProperty(property.name, flow_file) + | andThen(parsing::parseFloatingPoint) + | orThrow(fmt::format("Expected parsable double from \"{}\"", property.name)); +} + +inline float parseF32Property(const core::ProcessContext& ctx, const core::PropertyReference& property, const core::FlowFile* flow_file = nullptr) { + return ctx.getProperty(property.name, flow_file) + | andThen(parsing::parseFloatingPoint) + | orThrow(fmt::format("Expected parsable float from \"{}\"", property.name)); +} + inline std::chrono::milliseconds parseDurationProperty(const core::ProcessContext& ctx, const core::PropertyReference& property, const core::FlowFile* flow_file = nullptr) { return ctx.getProperty(property.name, flow_file) | andThen(parsing::parseDuration) @@ -133,7 +145,7 @@ inline std::optional parseOptionalFloatProperty(const core::ProcessContex if (property_str->empty()) { return std::nullopt; } - return parsing::parseFloat(*property_str) + return parsing::parseFloatingPoint(*property_str) | utils::orThrow(fmt::format("Expected parsable float from \"{}\"", property.name)); } return std::nullopt; diff --git a/extensions/llamacpp/processors/RunLlamaCppInference.h b/extensions/llamacpp/processors/RunLlamaCppInference.h index 16cf7a9c68..67cd14a9ee 100644 --- a/extensions/llamacpp/processors/RunLlamaCppInference.h +++ b/extensions/llamacpp/processors/RunLlamaCppInference.h @@ -83,6 +83,7 @@ class RunLlamaCppInference : public api::core::ProcessorImpl { .build(); EXTENSIONAPI static constexpr auto MinP = core::PropertyDefinitionBuilder<>::createProperty("Min P") .withDescription("Sets a minimum base probability threshold for token selection. 0.0 = disabled.") + .withValidator(core::StandardPropertyValidators::NUMBER_VALIDATOR) .build(); EXTENSIONAPI static constexpr auto MinKeep = core::PropertyDefinitionBuilder<>::createProperty("Min Keep") .withDescription("If greater than 0, force samplers to return N possible tokens at minimum.") diff --git a/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp b/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp index 3582c21909..fb22075214 100644 --- a/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp +++ b/extensions/llamacpp/tests/RunLlamaCppInferenceTests.cpp @@ -233,10 +233,6 @@ TEST_CASE("Invalid values for optional double type properties throw exception") REQUIRE(controller.getProcessor()->setProperty(processors::RunLlamaCppInference::TopP.name, "invalid_value")); property_name = processors::RunLlamaCppInference::TopP.name; } - SECTION("Invalid value for Min P property") { - REQUIRE(controller.getProcessor()->setProperty(processors::RunLlamaCppInference::MinP.name, "invalid_value")); - property_name = processors::RunLlamaCppInference::MinP.name; - } REQUIRE_THROWS(controller.trigger(minifi::test::InputFlowFileData{.content = "42", .attributes = {}})); CHECK(minifi::test::utils::verifyLogLinePresenceInPollTime(1s, diff --git a/extensions/python/ExecutePythonProcessor.cpp b/extensions/python/ExecutePythonProcessor.cpp index 184acad784..7ef4ae73a4 100644 --- a/extensions/python/ExecutePythonProcessor.cpp +++ b/extensions/python/ExecutePythonProcessor.cpp @@ -138,7 +138,8 @@ enum class PropertyValidatorCode : int64_t { DATA_SIZE = 3, TIME_PERIOD = 4, NON_BLANK = 5, - PORT = 6 + PORT = 6, + NUMBER = 7 }; const core::PropertyValidator& translateCodeToPropertyValidator(const PropertyValidatorCode& code) { @@ -156,6 +157,8 @@ const core::PropertyValidator& translateCodeToPropertyValidator(const PropertyVa return core::StandardPropertyValidators::NON_BLANK_VALIDATOR; case PropertyValidatorCode::PORT: return core::StandardPropertyValidators::PORT_VALIDATOR; + case PropertyValidatorCode::NUMBER: + return core::StandardPropertyValidators::NUMBER_VALIDATOR; default: throw std::invalid_argument("Unknown PropertyValidatorCode"); } diff --git a/extensions/python/pythonprocessors/nifiapi/properties.py b/extensions/python/pythonprocessors/nifiapi/properties.py index 3cbd4c58c3..fe0dab092f 100644 --- a/extensions/python/pythonprocessors/nifiapi/properties.py +++ b/extensions/python/pythonprocessors/nifiapi/properties.py @@ -53,6 +53,9 @@ def createRegexValidator(self, *args) -> int: def createLongValidator(self, *args) -> int: return StandardValidators.LONG_VALIDATOR + def createNumberValidator(self, *args) -> int: + return StandardValidators.NUMBER_VALIDATOR + class StandardValidators: _standard_validators = ValidatorGenerator() @@ -87,6 +90,7 @@ class MinifiPropertyTypes: TIME_PERIOD_TYPE = 4 NON_BLANK_TYPE = 5 PORT_TYPE = 6 + NUMBER_TYPE = 7 def translateStandardValidatorToMiNiFiPropertype(validators: List[int]) -> int: @@ -108,6 +112,8 @@ def translateStandardValidatorToMiNiFiPropertype(validators: List[int]) -> int: return MinifiPropertyTypes.NON_BLANK_TYPE if validator == StandardValidators.PORT_VALIDATOR: return MinifiPropertyTypes.PORT_TYPE + if validator == StandardValidators.NUMBER_VALIDATOR: + return MinifiPropertyTypes.NUMBER_TYPE return None diff --git a/libminifi/src/minifi-api.cpp b/libminifi/src/minifi-api.cpp index 1b484341c2..b1cc566443 100644 --- a/libminifi/src/minifi-api.cpp +++ b/libminifi/src/minifi-api.cpp @@ -88,6 +88,7 @@ gsl::not_null toPropertyValidator(minifi case MINIFI_VALIDATOR_UNSIGNED_INTEGER: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::UNSIGNED_INTEGER_VALIDATOR); case MINIFI_VALIDATOR_DATA_SIZE: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::DATA_SIZE_VALIDATOR); case MINIFI_VALIDATOR_PORT: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::PORT_VALIDATOR); + case MINIFI_VALIDATOR_NUMBER: return gsl::make_not_null(&minifi::core::StandardPropertyValidators::NUMBER_VALIDATOR); } gsl_FailFast(); } diff --git a/libminifi/test/unit/PropertyValidationTests.cpp b/libminifi/test/unit/PropertyValidationTests.cpp index 62770fa9b9..0749551912 100644 --- a/libminifi/test/unit/PropertyValidationTests.cpp +++ b/libminifi/test/unit/PropertyValidationTests.cpp @@ -224,4 +224,16 @@ TEST_CASE("TimePeriodValue Property") { CHECK(component.setProperty(property.getName(), "20").error() == core::PropertyErrorCode::ValidationFailed); } +TEST_CASE("Number validator") { + static constexpr auto property_definition = PropertyDefinitionBuilder<>::createProperty("prop").withValidator(core::StandardPropertyValidators::NUMBER_VALIDATOR).build(); + const Property property{property_definition}; + TestConfigurableComponent component; + component.setSupportedProperties(std::array{property_definition}); + CHECK(component.setProperty(property.getName(), "20")); + CHECK(component.setProperty(property.getName(), "3.14")); + CHECK(component.setProperty(property.getName(), "0.0000000001")); + CHECK_FALSE(component.setProperty(property.getName(), "10 000")); + CHECK_FALSE(component.setProperty(property.getName(), "20 apples")); +} + } // namespace org::apache::nifi::minifi::core diff --git a/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h b/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h index 2d91905ffa..06f66e9c28 100644 --- a/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h +++ b/minifi-api/common/include/minifi-cpp/core/PropertyValidator.h @@ -25,27 +25,31 @@ namespace org::apache::nifi::minifi::core { class PropertyValidator { public: virtual constexpr ~PropertyValidator() {} // NOLINT can't use = default because of gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93413 + PropertyValidator() = default; + PropertyValidator(const PropertyValidator&) = delete; + PropertyValidator(PropertyValidator&&) = delete; + PropertyValidator& operator=(const PropertyValidator& other) = delete; + PropertyValidator& operator=(PropertyValidator&& other) = delete; [[nodiscard]] virtual std::optional getEquivalentNifiStandardValidatorName() const = 0; [[nodiscard]] virtual bool validate(std::string_view input) const = 0; }; - class AlwaysValidValidator final : public PropertyValidator { public: - AlwaysValidValidator() = default; - constexpr ~AlwaysValidValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "VALID"; } - [[nodiscard]] bool validate(std::string_view) const override { return true; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "VALID"; + } + [[nodiscard]] bool validate(std::string_view) const override { + return true; + } }; class NonBlankValidator final : public PropertyValidator { public: - NonBlankValidator() = default; - constexpr ~NonBlankValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "NON_BLANK_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "NON_BLANK_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { return !utils::string::trim(input).empty(); } @@ -53,10 +57,9 @@ class NonBlankValidator final : public PropertyValidator { class TimePeriodValidator final : public PropertyValidator { public: - TimePeriodValidator() = default; - constexpr ~TimePeriodValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "TIME_PERIOD_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "TIME_PERIOD_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { const auto parsed_time = parsing::parseDuration(input); return parsed_time.has_value(); @@ -65,10 +68,9 @@ class TimePeriodValidator final : public PropertyValidator { class BooleanValidator final : public PropertyValidator { public: - BooleanValidator() = default; - constexpr ~BooleanValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "BOOLEAN_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "BOOLEAN_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { const auto parsed_bool = parsing::parseBool(input); return parsed_bool.has_value(); @@ -77,10 +79,9 @@ class BooleanValidator final : public PropertyValidator { class IntegerValidator final : public PropertyValidator { public: - IntegerValidator() = default; - constexpr ~IntegerValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "INTEGER_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "INTEGER_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { const auto parsed_integer = parsing::parseIntegral(input); return parsed_integer.has_value(); @@ -89,10 +90,9 @@ class IntegerValidator final : public PropertyValidator { class UnsignedIntegerValidator final : public PropertyValidator { public: - UnsignedIntegerValidator() = default; - constexpr ~UnsignedIntegerValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "NON_NEGATIVE_INTEGER_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "NON_NEGATIVE_INTEGER_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { const auto parsed_integer = parsing::parseIntegral(input); return parsed_integer.has_value(); @@ -101,10 +101,9 @@ class UnsignedIntegerValidator final : public PropertyValidator { class DataSizeValidator final : public PropertyValidator { public: - DataSizeValidator() = default; - constexpr ~DataSizeValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "DATA_SIZE_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "DATA_SIZE_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { const auto parsed_data_size = parsing::parseDataSize(input); return parsed_data_size.has_value(); @@ -113,16 +112,26 @@ class DataSizeValidator final : public PropertyValidator { class PortValidator final : public core::PropertyValidator { public: - PortValidator() = default; - constexpr ~PortValidator() override {} // NOLINT see comment at parent - - [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { return "PORT_VALIDATOR"; } + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "PORT_VALIDATOR"; + } [[nodiscard]] bool validate(const std::string_view input) const override { const auto parsed_integer = parsing::parseIntegralMinMax(input, 0, 65535); return parsed_integer.has_value(); } }; +class NumberValidator final : public core::PropertyValidator { + public: + [[nodiscard]] std::optional getEquivalentNifiStandardValidatorName() const override { + return "NUMBER_VALIDATOR"; + } + [[nodiscard]] bool validate(const std::string_view input) const override { + const auto parsed_number = parsing::parseFloatingPoint(input); + return parsed_number.has_value(); + } +}; + namespace StandardPropertyValidators { inline constexpr auto ALWAYS_VALID_VALIDATOR = AlwaysValidValidator{}; inline constexpr auto NON_BLANK_VALIDATOR = NonBlankValidator{}; @@ -132,6 +141,7 @@ inline constexpr auto INTEGER_VALIDATOR = IntegerValidator{}; inline constexpr auto UNSIGNED_INTEGER_VALIDATOR = UnsignedIntegerValidator{}; inline constexpr auto DATA_SIZE_VALIDATOR = DataSizeValidator{}; inline constexpr auto PORT_VALIDATOR = PortValidator{}; -} +inline constexpr auto NUMBER_VALIDATOR = NumberValidator{}; +} // namespace StandardPropertyValidators } // namespace org::apache::nifi::minifi::core diff --git a/minifi-api/include/minifi-api.h b/minifi-api/include/minifi-api.h index 2ee6f87aba..6f4cf5bcd7 100644 --- a/minifi-api/include/minifi-api.h +++ b/minifi-api/include/minifi-api.h @@ -119,6 +119,7 @@ enum minifi_validator : uint32_t { MINIFI_VALIDATOR_UNSIGNED_INTEGER = 5, MINIFI_VALIDATOR_DATA_SIZE = 6, MINIFI_VALIDATOR_PORT = 7, + MINIFI_VALIDATOR_NUMBER = 8, }; struct minifi_property_definition {