diff --git a/src/bindings/js/node/include/model_wrap.hpp b/src/bindings/js/node/include/model_wrap.hpp index cda9ff8b6ee65a..1bf69f69c653ea 100644 --- a/src/bindings/js/node/include/model_wrap.hpp +++ b/src/bindings/js/node/include/model_wrap.hpp @@ -109,6 +109,21 @@ class ModelWrap : public Napi::ObjectWrap { */ Napi::Value get_output_shape(const Napi::CallbackInfo& info); + /** + * @brief Returns a cloned model for the current model + * @param info Contains information about the environment and passed arguments + * @return Napi::Value Cloned model returned from the API + */ + Napi::Value clone(const Napi::CallbackInfo& info); + + /** + * @brief Creates JavaScript Model object and wraps inside of it ov::Model object. + * @param env The environment in which to construct a JavaScript object. + * @param model std::shared_ptr object to wrap + * @return A Javascript Model as Napi::Object. (Not ModelWrap object) + */ + static Napi::Object wrap(Napi::Env env, const std::shared_ptr model); + private: std::shared_ptr _model; ov::Core _core; diff --git a/src/bindings/js/node/lib/addon.ts b/src/bindings/js/node/lib/addon.ts index b5909ea9f3ae03..d4f7b8f7b067db 100644 --- a/src/bindings/js/node/lib/addon.ts +++ b/src/bindings/js/node/lib/addon.ts @@ -214,6 +214,10 @@ interface CoreConstructor { * A user-defined model read by {@link Core.readModel}. */ interface Model { + /** + * It returns a cloned model + */ + clone(): Model; /** * It gets the friendly name for a model. If a friendly name is not set * via {@link Model.setFriendlyName}, a unique model name is returned. diff --git a/src/bindings/js/node/src/model_wrap.cpp b/src/bindings/js/node/src/model_wrap.cpp index e8359b83ff6da3..4727cc099dfb7c 100644 --- a/src/bindings/js/node/src/model_wrap.cpp +++ b/src/bindings/js/node/src/model_wrap.cpp @@ -25,6 +25,7 @@ Napi::Function ModelWrap::get_class(Napi::Env env) { InstanceMethod("setFriendlyName", &ModelWrap::set_friendly_name), InstanceMethod("getFriendlyName", &ModelWrap::get_friendly_name), InstanceMethod("getOutputShape", &ModelWrap::get_output_shape), + InstanceMethod("clone", &ModelWrap::clone), InstanceAccessor<&ModelWrap::get_inputs>("inputs"), InstanceAccessor<&ModelWrap::get_outputs>("outputs")}); } @@ -171,3 +172,23 @@ Napi::Value ModelWrap::get_output_shape(const Napi::CallbackInfo& info) { return info.Env().Undefined(); } } + +Napi::Value ModelWrap::clone(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + if (info.Length() > 0) { + reportError(env, "clone() does not accept any arguments."); + return env.Undefined(); + } + return ModelWrap::wrap(env, _model->clone()); +} + +Napi::Object ModelWrap::wrap(Napi::Env env, const std::shared_ptr model) { + Napi::HandleScope scope(env); + const auto& prototype = env.GetInstanceData()->model; + if (!prototype) { + OPENVINO_THROW("Invalid pointer to Model prototype."); + } + auto obj = prototype.New({}); + Napi::ObjectWrap::Unwrap(obj)->set_model(model); + return obj; +} \ No newline at end of file diff --git a/src/bindings/js/node/tests/unit/model.test.js b/src/bindings/js/node/tests/unit/model.test.js index 7769bfa2beba1a..d6f2ae1666fe1e 100644 --- a/src/bindings/js/node/tests/unit/model.test.js +++ b/src/bindings/js/node/tests/unit/model.test.js @@ -9,6 +9,7 @@ const { getModelPath } = require('./utils.js'); const testXml = getModelPath().xml; const core = new ov.Core(); const model = core.readModelSync(testXml); +const clonedModel = model.clone(); describe('Node.js Model.isDynamic()', () => { it('should return a boolean value indicating if the model is dynamic', () => { @@ -112,3 +113,20 @@ describe('Model.getOutputSize()', () => { assert.strictEqual(model.getOutputSize(), 1, 'Expected getOutputSize to return 1 for the default model'); }); }); + +describe('Model.clone()', () => { + + it('should return an object of type model', () => { + assert.ok(clonedModel instanceof ov.Model, 'clone() should return a model'); + }); + + it('should return a model that is a clone of the calling model', () => { + assert.deepStrictEqual(clonedModel, model, "Cloned Model should be exactly equal to the calling model"); + }); + + it('should not accept any arguments', () => { + assert.throws(() => { + model.clone('unexpected argument'); + }, /^Error: clone\(\) does not accept any arguments\.$/, 'Expected clone to throw an error when called with arguments'); + }); +});