Skip to content

Commit cac2777

Browse files
authored
[JS API] Add config param to Core.import_model() (#24023)
### Details: - add `Core.import_model(stream, device_name, config[optional}) - add ` ov::AnyMap to_anyMap(const Napi::Env&, const Napi::Value&)` helper for conversion from Napi::Value to ov::AnyMap and reuse it in other methods ### Tickets: - *136460*
1 parent 6029d10 commit cac2777

File tree

5 files changed

+119
-52
lines changed

5 files changed

+119
-52
lines changed

src/bindings/js/node/include/helper.hpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ bool acceptableType(const Napi::Value& val, const std::vector<napi_types>& accep
172172

173173
Napi::Value any_to_js(const Napi::CallbackInfo& info, ov::Any value);
174174

175-
ov::Any js_to_any(const Napi::CallbackInfo& info, Napi::Value value);
175+
ov::Any js_to_any(const Napi::Env& env, const Napi::Value& value);
176176

177-
bool is_napi_value_int(const Napi::CallbackInfo& info, Napi::Value& num);
177+
bool is_napi_value_int(const Napi::Env& env, const Napi::Value& num);
178+
179+
ov::AnyMap to_anyMap(const Napi::Env&, const Napi::Value&);

src/bindings/js/node/lib/addon.ts

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ interface Core {
3737
readModelSync(modelPath: string, weightsPath?: string): Model;
3838
readModelSync(modelBuffer: Uint8Array, weightsBuffer?: Uint8Array): Model;
3939
importModelSync(modelStream: Buffer, device: string): CompiledModel;
40+
importModelSync(
41+
modelStream: Buffer,
42+
device: string,
43+
props: { [key: string]: string | number | boolean }
44+
): CompiledModel;
4045
getAvailableDevices(): string[];
4146
getVersions(deviceName: string): {
4247
[deviceName: string]: {

src/bindings/js/node/src/core_wrap.cpp

+31-28
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,14 @@ std::tuple<ov::AnyMap, std::string> try_get_set_property_parameters(const Napi::
2727
validate_set_property_args(info);
2828

2929
std::string device_name;
30-
ov::AnyMap properties;
3130

3231
const size_t args_length = info.Length();
3332

3433
if (args_length > 1)
3534
device_name = info[0].ToString();
3635

3736
const size_t parameters_position_index = device_name.empty() ? 0 : 1;
38-
Napi::Object parameters = info[parameters_position_index].ToObject();
39-
const auto& keys = parameters.GetPropertyNames();
40-
41-
for (uint32_t i = 0; i < keys.Length(); ++i) {
42-
auto property_name = static_cast<Napi::Value>(keys[i]).ToString().Utf8Value();
43-
44-
ov::Any any_value = js_to_any(info, parameters.Get(property_name));
45-
46-
properties.insert(std::make_pair(property_name, any_value));
47-
}
37+
const auto& properties = to_anyMap(info.Env(), info[parameters_position_index]);
4838

4939
return std::make_tuple(properties, device_name);
5040
}
@@ -301,25 +291,38 @@ Napi::Value CoreWrap::get_versions(const Napi::CallbackInfo& info) {
301291
}
302292

303293
Napi::Value CoreWrap::import_model(const Napi::CallbackInfo& info) {
304-
if (info.Length() != 2) {
305-
reportError(info.Env(), "Invalid number of arguments -> " + std::to_string(info.Length()));
306-
return info.Env().Undefined();
307-
}
308-
if (!info[0].IsBuffer()) {
309-
reportError(info.Env(), "The first argument must be of type Buffer.");
310-
return info.Env().Undefined();
311-
}
312-
if (!info[1].IsString()) {
313-
reportError(info.Env(), "The second argument must be of type String.");
294+
try {
295+
if (!info[0].IsBuffer()) {
296+
OPENVINO_THROW("The first argument must be of type Buffer.");
297+
}
298+
if (!info[1].IsString()) {
299+
OPENVINO_THROW("The second argument must be of type String.");
300+
}
301+
const auto& model_data = info[0].As<Napi::Buffer<uint8_t>>();
302+
const auto model_stream = std::string(reinterpret_cast<char*>(model_data.Data()), model_data.Length());
303+
std::stringstream _stream;
304+
_stream << model_stream;
305+
306+
ov::CompiledModel compiled;
307+
switch (info.Length()) {
308+
case 2: {
309+
compiled = _core.import_model(_stream, std::string(info[1].ToString()));
310+
break;
311+
}
312+
case 3: {
313+
compiled = _core.import_model(_stream, std::string(info[1].ToString()), to_anyMap(info.Env(), info[2]));
314+
break;
315+
}
316+
default: {
317+
OPENVINO_THROW("Invalid number of arguments -> " + std::to_string(info.Length()));
318+
}
319+
}
320+
return CompiledModelWrap::wrap(info.Env(), compiled);
321+
322+
} catch (std::exception& e) {
323+
reportError(info.Env(), e.what());
314324
return info.Env().Undefined();
315325
}
316-
const auto& model_data = info[0].As<Napi::Buffer<uint8_t>>();
317-
const auto model_stream = std::string(reinterpret_cast<char*>(model_data.Data()), model_data.Length());
318-
std::stringstream _stream;
319-
_stream << model_stream;
320-
321-
const auto& compiled = _core.import_model(_stream, std::string(info[1].ToString()));
322-
return CompiledModelWrap::wrap(info.Env(), compiled);
323326
}
324327

325328
Napi::Value CoreWrap::set_property(const Napi::CallbackInfo& info) {

src/bindings/js/node/src/helper.cpp

+23-12
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ Napi::Value any_to_js(const Napi::CallbackInfo& info, ov::Any value) {
510510
return info.Env().Undefined();
511511
}
512512

513-
ov::Any js_to_any(const Napi::CallbackInfo& info, Napi::Value value) {
513+
ov::Any js_to_any(const Napi::Env& env, const Napi::Value& value) {
514514
if (value.IsString()) {
515515
return ov::Any(value.ToString().Utf8Value());
516516
} else if (value.IsBigInt()) {
@@ -526,7 +526,7 @@ ov::Any js_to_any(const Napi::CallbackInfo& info, Napi::Value value) {
526526
} else if (value.IsNumber()) {
527527
Napi::Number num = value.ToNumber();
528528

529-
if (is_napi_value_int(info, value)) {
529+
if (is_napi_value_int(env, value)) {
530530
return ov::Any(num.Int32Value());
531531
} else {
532532
return ov::Any(num.DoubleValue());
@@ -538,14 +538,25 @@ ov::Any js_to_any(const Napi::CallbackInfo& info, Napi::Value value) {
538538
}
539539
}
540540

541-
bool is_napi_value_int(const Napi::CallbackInfo& info, Napi::Value& num) {
542-
return info.Env()
543-
.Global()
544-
.Get("Number")
545-
.ToObject()
546-
.Get("isInteger")
547-
.As<Napi::Function>()
548-
.Call({num})
549-
.ToBoolean()
550-
.Value();
541+
bool is_napi_value_int(const Napi::Env& env, const Napi::Value& num) {
542+
return env.Global().Get("Number").ToObject().Get("isInteger").As<Napi::Function>().Call({num}).ToBoolean().Value();
543+
}
544+
545+
ov::AnyMap to_anyMap(const Napi::Env& env, const Napi::Value& val) {
546+
ov::AnyMap properties;
547+
if (!val.IsObject()) {
548+
OPENVINO_THROW("Passed Napi::Value must be an object.");
549+
}
550+
const auto& parameters = val.ToObject();
551+
const auto& keys = parameters.GetPropertyNames();
552+
553+
for (uint32_t i = 0; i < keys.Length(); ++i) {
554+
const auto& property_name = static_cast<Napi::Value>(keys[i]).ToString().Utf8Value();
555+
556+
ov::Any any_value = js_to_any(env, parameters.Get(property_name));
557+
558+
properties.insert(std::make_pair(property_name, any_value));
559+
}
560+
561+
return properties;
551562
}

src/bindings/js/node/tests/basic.test.js

+56-10
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ const compiledModel = core.compileModelSync(model, 'CPU');
1414
const modelLike = [[model],
1515
[compiledModel]];
1616

17-
it('Core.getAvailableDevices()', () => {
18-
const devices = core.getAvailableDevices();
19-
20-
assert.ok(devices.includes('CPU'));
17+
it('Core.getAvailableDevices()', () => {
18+
const devices = core.getAvailableDevices();
19+
20+
assert.ok(devices.includes('CPU'));
2121
});
2222

2323
describe('Core.getVersions()', () => {
@@ -214,16 +214,62 @@ describe('Input class for ov::Input<const ov::Node>', () => {
214214

215215
});
216216

217-
it('Test exportModel()/importModel()', () => {
217+
describe('Test exportModel()/importModel()', () => {
218218
const userStream = compiledModel.exportModelSync();
219-
const newCompiled = core.importModelSync(userStream, 'CPU');
220219
const epsilon = 0.5;
221220
const tensor = Float32Array.from({ length: 3072 }, () => (Math.random() + epsilon));
222-
223221
const inferRequest = compiledModel.createInferRequest();
224222
const res1 = inferRequest.infer([tensor]);
225-
const newInferRequest = newCompiled.createInferRequest();
226-
const res2 = newInferRequest.infer([tensor]);
227223

228-
assert.deepStrictEqual(res1['fc_out'].data[0], res2['fc_out'].data[0]);
224+
it('Test importModel(stream, device)', () => {
225+
const newCompiled = core.importModelSync(userStream, 'CPU');
226+
const newInferRequest = newCompiled.createInferRequest();
227+
const res2 = newInferRequest.infer([tensor]);
228+
229+
assert.deepStrictEqual(res1['fc_out'].data[0], res2['fc_out'].data[0]);
230+
});
231+
232+
it('Test importModel(stream, device, config)', () => {
233+
const newCompiled = core.importModelSync(userStream, 'CPU', { 'NUM_STREAMS': 1 });
234+
const newInferRequest = newCompiled.createInferRequest();
235+
const res2 = newInferRequest.infer([tensor]);
236+
237+
assert.deepStrictEqual(res1['fc_out'].data[0], res2['fc_out'].data[0]);
238+
});
239+
240+
it('Test importModel(stream, device) throws', () => {
241+
assert.throws(
242+
() => core.importModelSync(epsilon, 'CPU'),
243+
/The first argument must be of type Buffer./
244+
);
245+
});
246+
247+
it('Test importModel(stream, device) throws', () => {
248+
assert.throws(
249+
() => core.importModelSync(userStream, tensor),
250+
/The second argument must be of type String./
251+
);
252+
});
253+
it('Test importModel(stream, device, config: tensor) throws', () => {
254+
assert.throws(
255+
() => core.importModelSync(userStream, 'CPU', tensor),
256+
/NotFound: Unsupported property 0 by CPU plugin./
257+
);
258+
});
259+
260+
it('Test importModel(stream, device, config: string) throws', () => {
261+
const testString = 'test';
262+
assert.throws(
263+
() => core.importModelSync(userStream, 'CPU', testString),
264+
/Passed Napi::Value must be an object./
265+
);
266+
});
267+
268+
it('Test importModel(stream, device, config: unsupported property) throws', () => {
269+
const tmpDir = '/tmp';
270+
assert.throws(
271+
() => core.importModelSync(userStream, 'CPU', {'CACHE_DIR': tmpDir}),
272+
/Unsupported property CACHE_DIR by CPU plugin./
273+
);
274+
});
229275
});

0 commit comments

Comments
 (0)