Skip to content

Commit

Permalink
[OV JS] Expose Model.clone to Node.js Api
Browse files Browse the repository at this point in the history
Changes as part of this commit include:
* Add a ModelWrap::clone function:
  Calls the underlying Model.clone function
* Add a ModelWrap::Wrap function to return the
  cloned model as a Napi::Value
* Update the addon.ts file with the clone method
* Add unit tests for the clone Api

Resolves: openvinotoolkit#25402

Signed-off-by: Nashez Zubair <[email protected]>
  • Loading branch information
nashez committed Jul 28, 2024
1 parent 3056b53 commit 0ddfb3d
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/bindings/js/node/include/model_wrap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ class ModelWrap : public Napi::ObjectWrap<ModelWrap> {
*/
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<ov::Model> object to wrap
* @return A Javascript Model as Napi::Object. (Not ModelWrap object)
*/
static Napi::Object wrap(Napi::Env env, const std::shared_ptr<ov::Model> model);

private:
std::shared_ptr<ov::Model> _model;
ov::Core _core;
Expand Down
4 changes: 4 additions & 0 deletions src/bindings/js/node/lib/addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions src/bindings/js/node/src/model_wrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")});
}
Expand Down Expand Up @@ -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<ov::Model> model) {
Napi::HandleScope scope(env);
const auto& prototype = env.GetInstanceData<AddonData>()->model;
if (!prototype) {
OPENVINO_THROW("Invalid pointer to Model prototype.");
}
auto obj = prototype.New({});
Napi::ObjectWrap<ModelWrap>::Unwrap(obj)->set_model(model);
return obj;
}
18 changes: 18 additions & 0 deletions src/bindings/js/node/tests/unit/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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');
});
});

0 comments on commit 0ddfb3d

Please sign in to comment.