Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions model_api/docs/source/guides/model-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

Model's static method `create_model()` has two overloads. One constructs the model from a string (a path or a model name) and the other takes an already constructed `InferenceAdapter`. The first overload configures a created model with values taken from `configuration` dict function argument and from model's intermediate representation (IR) stored in `.xml` in `model_info` section of `rt_info`. Values provided in `configuration` have priority over values in IR `rt_info`. If no value is specified in `configuration` nor in `rt_info` the default value for a model wrapper is used. For Python configuration values are accessible as model wrapper member fields.

## Runtime parameter modification

Parameters can be read and modified after model creation using `get_param()` and `set_param()`:

```python
from model_api.models import Model

model = Model.create_model("model.xml")

# Read current value
current_threshold = model.get_param("confidence_threshold")

# Modify parameter with validation
model.set_param("confidence_threshold", 0.7)

# Parameters are also accessible via the params descriptor
print(model.params.confidence_threshold)
```

`set_param()` validates the value against the parameter definition and raises `WrapperError` if validation fails. Unknown parameter names are logged as warnings and ignored.

## List of values

The list features only model wrappers which introduce new configuration values in their hierarchy.
Expand Down
37 changes: 24 additions & 13 deletions model_api/src/model_api/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,29 @@ def get_param(self, name: str) -> Any:
return self._parameters_cache[name].default_value
return self.raise_error(f"Parameter '{name}' not found")

def set_param(self, name: str, value: Any) -> None:
"""Sets a parameter value, validating it against the parameter definition.

Args:
name (str): parameter name
value (Any): parameter value
"""
parameters = self.parameters()

if name in parameters:
errors = parameters[name].validate(value)
if errors:
self.logger.error(f'Error with "{name}" parameter:')
for _error in errors:
self.logger.error(f"\t{_error}")
self.raise_error("Incorrect user configuration")
value = parameters[name].get_value(value)
self.__setattr__(f"_{name}", value)
else:
self.logger.warning(
f'The parameter "{name}" not found in {self.__model__} wrapper, will be omitted',
)

def get_cached_parameters(self) -> dict[str, Any]:
"""Get cached parameters, initializing cache if needed.

Expand Down Expand Up @@ -432,19 +455,7 @@ def _load_config(self, config: dict[str, Any]) -> None:
for name, value in config.items():
if value is None:
continue
if name in parameters:
errors = parameters[name].validate(value)
if errors:
self.logger.error(f'Error with "{name}" parameter:')
for _error in errors:
self.logger.error(f"\t{_error}")
self.raise_error("Incorrect user configuration")
value = parameters[name].get_value(value)
self.__setattr__(f"_{name}", value)
else:
self.logger.warning(
f'The parameter "{name}" not found in {self.__model__} wrapper, will be omitted',
)
self.set_param(name, value)

@classmethod
def raise_error(cls, message) -> NoReturn:
Expand Down
43 changes: 43 additions & 0 deletions model_api/tests/unit/models/test_model_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,49 @@ def test_get_param_unknown_raises(self):
model.get_param("nonexistent_parameter_xyz")


class TestModelSetParam:
"""Tests for set_param."""

def test_set_param_valid_value(self):
"""Valid param sets underscore-prefixed attribute."""
from model_api.models.image_model import ImageModel

adapter = _make_adapter()
model = ImageModel(adapter, configuration={}, preload=False)
model.set_param("resize_type", "fit_to_window")
assert model.params.resize_type == "fit_to_window"
assert model.get_param("resize_type") == "fit_to_window"

def test_set_param_invalid_value_raises(self):
"""Validation failure raises WrapperError."""
from model_api.models.image_model import ImageModel

adapter = _make_adapter()
model = ImageModel(adapter, configuration={}, preload=False)
with pytest.raises(WrapperError, match="Incorrect user configuration"):
model.set_param("resize_type", "INVALID_RESIZE_TYPE")

def test_set_param_unknown_warns(self, caplog):
"""Unknown param logs warning and is omitted."""
adapter = _make_adapter()
model = Model(adapter, configuration={}, preload=False)
with caplog.at_level(logging.WARNING):
model.set_param("unknown_param_xyz", 42)
assert any("unknown_param_xyz" in r.message for r in caplog.records)
assert any("not found" in r.message for r in caplog.records)
assert not hasattr(model, "_unknown_param_xyz")

def test_set_param_applies_get_value_transformation(self):
"""Value is transformed by parameter's get_value method."""
from model_api.models.image_model import ImageModel

adapter = _make_adapter()
model = ImageModel(adapter, configuration={}, preload=False)
# resize_type is a StringValue which returns value as-is, but validates options
model.set_param("resize_type", "standard")
assert model.params.resize_type == "standard"


class TestModelGetCachedParameters:
"""Tests for get_cached_parameters (lines 124-132)."""

Expand Down
Loading