diff --git a/src/bindings/js/node/include/compiled_model.hpp b/src/bindings/js/node/include/compiled_model.hpp index cc3a86eda2c624..f9c1927a3a90fb 100644 --- a/src/bindings/js/node/include/compiled_model.hpp +++ b/src/bindings/js/node/include/compiled_model.hpp @@ -68,6 +68,9 @@ class CompiledModelWrap : public Napi::ObjectWrap { */ Napi::Value get_inputs(const Napi::CallbackInfo& info); + /** @brief Exports the compiled model to bytes/output stream. */ + Napi::Value export_model(const Napi::CallbackInfo& info); + private: ov::CompiledModel _compiled_model; }; diff --git a/src/bindings/js/node/include/core_wrap.hpp b/src/bindings/js/node/include/core_wrap.hpp index ac4637c5faf620..c1db1c45c3e930 100644 --- a/src/bindings/js/node/include/core_wrap.hpp +++ b/src/bindings/js/node/include/core_wrap.hpp @@ -84,6 +84,9 @@ class CoreWrap : public Napi::ObjectWrap { const Napi::String& device, const std::map& config); + /** @brief Imports a compiled model from the previously exported one. */ + Napi::Value import_model(const Napi::CallbackInfo& info); + /** @brief Returns devices available for inference. */ Napi::Value get_available_devices(const Napi::CallbackInfo& info); diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index 7f4f9fd917cd13..eccb5c38e114f4 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -36,6 +36,7 @@ interface Core { modelBuffer: Uint8Array, weightsBuffer?: Uint8Array): Promise; readModelSync(modelPath: string, weightsPath?: string): Model; readModelSync(modelBuffer: Uint8Array, weightsBuffer?: Uint8Array): Model; + importModelSync(modelStream: Buffer, device: string): CompiledModel; getAvailableDevices(): string[]; } interface CoreConstructor { @@ -56,6 +57,7 @@ interface CompiledModel { output(nameOrId?: string | number): Output; input(nameOrId?: string | number): Output; createInferRequest(): InferRequest; + exportModelSync(): Buffer; } interface Tensor { diff --git a/src/bindings/js/node/src/compiled_model.cpp b/src/bindings/js/node/src/compiled_model.cpp index d02f14a82bae1c..304ac9d299a3f2 100644 --- a/src/bindings/js/node/src/compiled_model.cpp +++ b/src/bindings/js/node/src/compiled_model.cpp @@ -19,7 +19,8 @@ Napi::Function CompiledModelWrap::get_class(Napi::Env env) { InstanceMethod("input", &CompiledModelWrap::get_input), InstanceAccessor<&CompiledModelWrap::get_inputs>("inputs"), InstanceMethod("output", &CompiledModelWrap::get_output), - InstanceAccessor<&CompiledModelWrap::get_outputs>("outputs")}); + InstanceAccessor<&CompiledModelWrap::get_outputs>("outputs"), + InstanceMethod("exportModelSync", &CompiledModelWrap::export_model)}); } Napi::Object CompiledModelWrap::wrap(Napi::Env env, ov::CompiledModel compiled_model) { @@ -110,3 +111,10 @@ Napi::Value CompiledModelWrap::get_inputs(const Napi::CallbackInfo& info) { return js_inputs; } + +Napi::Value CompiledModelWrap::export_model(const Napi::CallbackInfo& info) { + std::stringstream _stream; + _compiled_model.export_model(_stream); + const auto& exported = _stream.str(); + return Napi::Buffer::Copy(info.Env(), exported.c_str(), exported.size()); +} diff --git a/src/bindings/js/node/src/core_wrap.cpp b/src/bindings/js/node/src/core_wrap.cpp index d5d5d68ed6b1a6..96d827e77431cf 100644 --- a/src/bindings/js/node/src/core_wrap.cpp +++ b/src/bindings/js/node/src/core_wrap.cpp @@ -20,6 +20,7 @@ Napi::Function CoreWrap::get_class(Napi::Env env) { InstanceMethod("readModel", &CoreWrap::read_model_async), InstanceMethod("compileModelSync", &CoreWrap::compile_model_sync_dispatch), InstanceMethod("compileModel", &CoreWrap::compile_model_async), + InstanceMethod("importModelSync", &CoreWrap::import_model), InstanceMethod("getAvailableDevices", &CoreWrap::get_available_devices)}); } @@ -230,3 +231,25 @@ Napi::Value CoreWrap::get_available_devices(const Napi::CallbackInfo& info) { return js_devices; } + +Napi::Value CoreWrap::import_model(const Napi::CallbackInfo& info) { + if (info.Length() != 2) { + reportError(info.Env(), "Invalid number of arguments -> " + std::to_string(info.Length())); + return info.Env().Undefined(); + } + if (!info[0].IsBuffer()) { + reportError(info.Env(), "The first argument must be of type Buffer."); + return info.Env().Undefined(); + } + if (!info[1].IsString()) { + reportError(info.Env(), "The second argument must be of type String."); + return info.Env().Undefined(); + } + const auto& model_data = info[0].As>(); + const auto model_stream = std::string(reinterpret_cast(model_data.Data()), model_data.Length()); + std::stringstream _stream; + _stream << model_stream; + + const auto& compiled = _core.import_model(_stream, std::string(info[1].ToString())); + return CompiledModelWrap::wrap(info.Env(), compiled); +} diff --git a/src/bindings/js/node/tests/basic.test.js b/src/bindings/js/node/tests/basic.test.js index 8711ba3d33e3cc..07e0502053edd1 100644 --- a/src/bindings/js/node/tests/basic.test.js +++ b/src/bindings/js/node/tests/basic.test.js @@ -186,3 +186,17 @@ describe('Input class for ov::Input', () => { }); }); + +it('Test exportModel()/importModel()', () => { + const userStream = compiledModel.exportModelSync(); + const newCompiled = core.importModelSync(userStream, 'CPU'); + const epsilon = 0.5; + const tensor = Float32Array.from({ length: 3072 }, () => (Math.random() + epsilon)); + + const inferRequest = compiledModel.createInferRequest(); + const res1 = inferRequest.infer([tensor]); + const newInferRequest = newCompiled.createInferRequest(); + const res2 = newInferRequest.infer([tensor]); + + assert.deepStrictEqual(res1['fc_out'].data[0], res2['fc_out'].data[0]); +});