diff --git a/src/bindings/python/src/openvino/_ov_api.py b/src/bindings/python/src/openvino/_ov_api.py index 7133f180deeeae..cf06667483cf81 100644 --- a/src/bindings/python/src/openvino/_ov_api.py +++ b/src/bindings/python/src/openvino/_ov_api.py @@ -632,14 +632,14 @@ def query_model( def import_model( self, - model_stream: bytes, + model_stream: Union[bytes, io.BytesIO, Tensor], device_name: str, config: Optional[dict[str, Any]] = None, ) -> CompiledModel: """Imports a compiled model from a previously exported one. - :param model_stream: Input stream, containing a model previously exported, using export_model method. - :type model_stream: bytes + :param model_stream: Input stream or tensor, containing a model previously exported, using export_model method. + :type model_stream: Union[bytes, io.BytesIO, openvino.Tensor] :param device_name: Name of device to which compiled model is imported. Note: if device_name is not used to compile the original model, an exception is thrown. diff --git a/src/bindings/python/src/pyopenvino/core/core.cpp b/src/bindings/python/src/pyopenvino/core/core.cpp index 3d0b7c6add2e18..86d0166cf14c5d 100644 --- a/src/bindings/python/src/pyopenvino/core/core.cpp +++ b/src/bindings/python/src/pyopenvino/core/core.cpp @@ -533,6 +533,35 @@ void regclass_Core(py::module m) { :rtype: openvino.Model )"); + cls.def( + "import_model", + [](ov::Core& self, + const ov::Tensor& exported_blob, + const std::string& device_name, + const std::map& properties) { + const auto _properties = Common::utils::properties_to_any_map(properties); + ConditionalGILScopedRelease release; + return self.import_model(exported_blob, device_name, _properties); + }, + py::arg("tensor"), + py::arg("device_name"), + py::arg("properties"), + R"( + Imports a compiled model from a previously exported one. + + GIL is released while running this function. + + :param compiled_blob: ov::Tensor input blob containing a model previously exported using the ov::CompiledModel::export_model method. + :type compiled_blob: openvino.Tensor + :param device_name: Name of device to which compiled model is imported. + Note: if device_name is not used to compile the original model, an exception is thrown. + :type device_name: str + :param properties: Optional map of pairs: (property name, property value) relevant only for this load operation. + :type properties: dict[str, typing.Any], optional + :return: A compiled model. + :rtype: openvino.CompiledModel + )"); + cls.def( "import_model", [](ov::Core& self, diff --git a/src/bindings/python/tests/test_runtime/test_compiled_model.py b/src/bindings/python/tests/test_runtime/test_compiled_model.py index bee43090ac9629..634e72de2ac9d4 100644 --- a/src/bindings/python/tests/test_runtime/test_compiled_model.py +++ b/src/bindings/python/tests/test_runtime/test_compiled_model.py @@ -9,6 +9,7 @@ from tests.utils.helpers import ( get_relu_model, generate_image, + tensor_from_bytes, generate_model_and_image, generate_concat_compiled_model, generate_relu_compiled_model, @@ -38,7 +39,7 @@ def test_get_runtime_model(device): assert isinstance(runtime_model, Model) -def test_export_import(device): +def test_export_import_stream(device): core = Core() if props.device.Capability.EXPORT_IMPORT not in core.get_property(device, props.device.capabilities): @@ -56,6 +57,25 @@ def test_export_import(device): assert np.argmax(res[new_compiled.outputs[0]]) == 531 +def test_export_import_tensor(device): + core = Core() + + if props.device.Capability.EXPORT_IMPORT not in core.get_property(device, props.device.capabilities): + pytest.skip(f"{core.get_property(device, props.device.full_name)} plugin due-to export, import model API isn't implemented.") + + compiled_model = generate_relu_compiled_model(device) + + user_stream = compiled_model.export_model() + exported_model_tensor = tensor_from_bytes(user_stream) + + new_compiled = core.import_model(exported_model_tensor, device) + + img = generate_image() + res = new_compiled.infer_new_request({"data": img}) + + assert np.argmax(res[new_compiled.outputs[0]]) == 531 + + def test_export_import_with_encryption(device): core = Core() diff --git a/src/bindings/python/tests/utils/helpers.py b/src/bindings/python/tests/utils/helpers.py index 919b01b4186d5f..9185bdf65940ca 100644 --- a/src/bindings/python/tests/utils/helpers.py +++ b/src/bindings/python/tests/utils/helpers.py @@ -2,6 +2,7 @@ # Copyright (C) 2018-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +import io from typing import Union import os @@ -118,6 +119,11 @@ def generate_image(shape: tuple = (1, 3, 32, 32), dtype: Union[str, np.dtype] = return np.random.rand(*shape).astype(dtype) +def tensor_from_bytes(stream: io.BytesIO) -> Tensor: + stream.seek(0) + return Tensor(np.frombuffer(stream.getbuffer(), dtype=np.uint8)) + + def get_model_with_template_extension(): core = Core() ir = bytes(b"""