@coherentglobal/wasm-runner 0.0.103 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +137 -222
  2. package/dist/CancellationToken.js.map +1 -1
  3. package/dist/browser/logger.js.map +1 -1
  4. package/dist/browser/template/main.template.js +1 -1
  5. package/dist/browser/template/main.template.js.map +1 -1
  6. package/dist/browser/template/runtime.template.js.map +1 -1
  7. package/dist/browser/template/worker.template.js +7 -4
  8. package/dist/browser/template/worker.template.js.map +1 -1
  9. package/dist/browser/template.js +1 -1
  10. package/dist/browser/template.js.map +1 -1
  11. package/dist/browser.d.ts +2 -2
  12. package/dist/browser.js +6 -6
  13. package/dist/browser.js.map +1 -1
  14. package/dist/constants.d.ts +5 -0
  15. package/dist/constants.js +14 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/error.d.ts +5 -0
  18. package/dist/error.js +13 -3
  19. package/dist/error.js.map +1 -1
  20. package/dist/mockConstants.d.ts +5 -0
  21. package/dist/mockConstants.js +14 -0
  22. package/dist/mockConstants.js.map +1 -0
  23. package/dist/node/logger.d.ts +1 -4
  24. package/dist/node/logger.js +1 -1
  25. package/dist/node/logger.js.map +1 -1
  26. package/dist/node/logger.ts +1 -1
  27. package/dist/node/template/main.template.ejs +126 -86
  28. package/dist/node/threads/mockWorkerThread.d.ts +28 -0
  29. package/dist/node/threads/mockWorkerThread.js +89 -0
  30. package/dist/node/threads/mockWorkerThread.js.map +1 -0
  31. package/dist/node/threads/workerPool.d.ts +17 -0
  32. package/dist/node/threads/workerPool.js +27 -0
  33. package/dist/node/threads/workerPool.js.map +1 -0
  34. package/dist/node/threads/workerPool.ts +38 -0
  35. package/dist/node/threads/workerThread.d.ts +31 -0
  36. package/dist/node/threads/workerThread.js +91 -0
  37. package/dist/node/threads/workerThread.js.map +1 -0
  38. package/dist/node/threads/workerThread.ts +92 -0
  39. package/dist/node.d.ts +36 -26
  40. package/dist/node.js +334 -62
  41. package/dist/node.js.map +1 -1
  42. package/dist/responseTimeMetric.d.ts +16 -0
  43. package/dist/responseTimeMetric.js +56 -0
  44. package/dist/responseTimeMetric.js.map +1 -0
  45. package/dist/serializer/columnarSerializer.d.ts +3 -2
  46. package/dist/serializer/columnarSerializer.js +10 -1
  47. package/dist/serializer/columnarSerializer.js.map +1 -1
  48. package/dist/types.d.ts +41 -2
  49. package/dist/types.js +8 -0
  50. package/dist/types.js.map +1 -1
  51. package/dist/utils.d.ts +27 -0
  52. package/dist/utils.js +49 -13
  53. package/dist/utils.js.map +1 -1
  54. package/package.json +8 -5
package/dist/node.js CHANGED
@@ -51,10 +51,14 @@ const fs_1 = __importStar(require("fs"));
51
51
  const path_1 = __importDefault(require("path"));
52
52
  const util_1 = require("util");
53
53
  const got_1 = __importDefault(require("got"));
54
- const node_worker_threads_pool_1 = require("node-worker-threads-pool");
54
+ const semver_1 = __importDefault(require("semver"));
55
+ const workerPool_1 = __importDefault(require("./node/threads/workerPool"));
55
56
  const utils = __importStar(require("./utils"));
57
+ const types_1 = require("./types");
56
58
  const error_1 = __importDefault(require("./error"));
57
59
  const node_os_1 = require("node:os");
60
+ const constants_1 = require("./constants");
61
+ const responseTimeMetric_1 = __importDefault(require("./responseTimeMetric"));
58
62
  /* istanbul ignore next */
59
63
  var columnarSerializer_1 = require("./serializer/columnarSerializer");
60
64
  Object.defineProperty(exports, "ColumnarSerializer", { enumerable: true, get: function () { return columnarSerializer_1.ColumnarSerializer; } });
@@ -92,6 +96,7 @@ const unzipFiles = (id, url, workerFolder) => {
92
96
  let data;
93
97
  let metadata;
94
98
  let formspec;
99
+ let compilerLog;
95
100
  // @ts-ignore
96
101
  const stream = fs_1.default.createReadStream(url).pipe((0, unzipper_1.Parse)());
97
102
  return new Promise((resolve, reject) => {
@@ -115,6 +120,9 @@ const unzipFiles = (id, url, workerFolder) => {
115
120
  if (entry.path.toLowerCase().indexOf("_jsonformspec.json") > -1) {
116
121
  formspec = fileName;
117
122
  }
123
+ if (entry.path.toLowerCase().indexOf("_compilerlog.json") > -1) {
124
+ compilerLog = fileName;
125
+ }
118
126
  return entry.pipe(writeStream);
119
127
  }
120
128
  return entry.autodrain();
@@ -131,6 +139,7 @@ const unzipFiles = (id, url, workerFolder) => {
131
139
  data,
132
140
  metadata,
133
141
  formspec,
142
+ compilerLog,
134
143
  });
135
144
  });
136
145
  stream.on("error", (error) => reject(error));
@@ -140,18 +149,11 @@ const template = ejs_1.default.compile(fs_1.default.readFileSync(path_1.default.
140
149
  encoding: "utf8",
141
150
  flag: "r",
142
151
  }), { async: false });
143
- /**
144
- * WASM runner config
145
- * @typedef {Object} RunnerConfig
146
- * @property {string} id - Model id or version_uuid
147
- * @property {string} url - Model url
148
- */
149
- /**
150
- * Input object
151
- * @typedef {Object} InputObject
152
- * @property {Object} request_data - Request input date
153
- * @property {Object} request_meta - Request input meta
154
- */
152
+ const delay = (time) => {
153
+ return new Promise((res) => {
154
+ setTimeout(res, time);
155
+ });
156
+ };
155
157
  /**
156
158
  * WasmRunner class, responsible for managing model and it's lifecycle
157
159
  *
@@ -165,21 +167,30 @@ class WasmRunner {
165
167
  *
166
168
  * @param {RunnerConfig} wasmRunnerConfig
167
169
  */
168
- constructor(wasmRunnerConfig, externalResolverModule = "", license = "") {
170
+ constructor(wasmRunnerConfig, externalResolverModule = "", options = {}, license = "") {
171
+ let baseTmpDir = null;
172
+ if (process.env.DEV_DEBUG_TMP_PATH) {
173
+ baseTmpDir = path_1.default.join(process.cwd(), process.env.DEV_DEBUG_TMP_PATH);
174
+ }
169
175
  this._tempWorkerFolder = tmp_1.default.dirSync({
170
176
  prefix: "wasmserver_worker",
171
177
  // @ts-ignore
172
178
  mode: 0o750,
179
+ tmpdir: baseTmpDir,
173
180
  });
174
181
  this._tempModelFolder = tmp_1.default.dirSync({
175
182
  prefix: "wasmserver_model",
176
183
  // @ts-ignore
177
184
  mode: 0o750,
185
+ tmpdir: baseTmpDir,
178
186
  });
179
187
  this.license = license;
180
- this.externalResolverModule =
181
- externalResolverModule || require.resolve("./defaultExternalResolver");
188
+ this.initializingModels = [];
189
+ this.externalResolverModule = externalResolverModule
190
+ ? require(useCorrectPath(require.resolve(externalResolverModule)))
191
+ : require(require.resolve("./defaultExternalResolver"));
182
192
  this.models = [];
193
+ this.options = options;
183
194
  if (!utils.isEmpty(wasmRunnerConfig)) {
184
195
  if (Array.isArray(wasmRunnerConfig)) {
185
196
  this.models = wasmRunnerConfig.map((wsconfig) => ({
@@ -204,20 +215,24 @@ class WasmRunner {
204
215
  * @function initialize
205
216
  */
206
217
  /* istanbul ignore next */
207
- initialize(license = "") {
208
- return __awaiter(this, void 0, void 0, function* () {
218
+ initialize() {
219
+ return __awaiter(this, arguments, void 0, function* (license = "") {
220
+ if (this.options.compatibilityCheckEnabled) {
221
+ this.safeCompiler = yield this.getSafeCompilerVersion(this.options.runnerVersion);
222
+ }
209
223
  yield Promise.all(this.models.map((m) => __awaiter(this, void 0, void 0, function* () {
210
224
  return this._initializeModelInstance(m);
211
225
  })));
212
226
  });
213
227
  }
214
228
  /* istanbul ignore next */
215
- _createModelInstance(id, url, workerFolder, size) {
216
- return __awaiter(this, void 0, void 0, function* () {
229
+ _createModelInstance(id_1, url_1, workerFolder_1) {
230
+ return __awaiter(this, arguments, void 0, function* (id, url, workerFolder, size = 1) {
231
+ const workerSize = size || 1;
217
232
  logger_1.default.debug({
218
233
  TimeStamp: Date.now(),
219
234
  EventType: "Runner._createModelInstance",
220
- size: size,
235
+ size: workerSize,
221
236
  ModelId: id,
222
237
  });
223
238
  const tempFolder = path_1.default.join(workerFolder, `${id}-${Date.now()}`);
@@ -235,6 +250,11 @@ class WasmRunner {
235
250
  });
236
251
  const modelName = id;
237
252
  yield replaceRegexInFile(modelPack.js, `wasmBinaryFile="${path_1.default.basename(modelPack.wasm)}"`, `scriptDirectory="";wasmBinaryFile="${modelPack.wasm}"`);
253
+ const replaceIsFileURLFunc = "function isFileURI(filename) { if(typeof process !== undefined && process.versions && process.versions.node) { return true; }";
254
+ yield replaceRegexInFile(modelPack.js, "function isFileURI(filename) {", replaceIsFileURLFunc);
255
+ yield replaceRegexInFile(modelPack.js, "function isFileURI(filename){", replaceIsFileURLFunc);
256
+ // Remove `new URL()` since `isFileURI` will always be true for Node
257
+ yield replaceRegexInFile(modelPack.js, /\s*isFileURI\s*\(\s*filename\s*\)\s*\?\s*new\s*URL\s*\(\s*filename\s*\)\s*:\s*/g, "");
238
258
  if (modelPack.data) {
239
259
  yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
240
260
  yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
@@ -242,10 +262,13 @@ class WasmRunner {
242
262
  yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
243
263
  }
244
264
  // Prepare File Paths
245
- const externalResolverPath = useCorrectPath(this.externalResolverModule);
265
+ // const externalResolverPath = useCorrectPath(this.externalResolverModule);
246
266
  const loggerPath = useCorrectPath(process.env.NODE_ENV === "test"
247
267
  ? require.resolve("./node/mockLogger.js")
248
268
  : require.resolve("./node/logger"));
269
+ const workerThread = useCorrectPath(process.env.NODE_ENV === "test"
270
+ ? require.resolve("./node/threads/mockWorkerThread.js")
271
+ : require.resolve("./node/threads/workerThread"));
249
272
  let metadata = "undefined";
250
273
  if (!utils.isEmpty(modelPack.metadata)) {
251
274
  const metadataPath = useCorrectPath(require.resolve(modelPack.metadata));
@@ -256,6 +279,14 @@ class WasmRunner {
256
279
  const formSpecPath = useCorrectPath(require.resolve(modelPack.formspec));
257
280
  formspec = `require("${formSpecPath}")`;
258
281
  }
282
+ let compilerVersion = "undefined";
283
+ let compatibilityStatus = "undefined";
284
+ compilerVersion = !utils.isEmpty(modelPack.compilerLog)
285
+ ? yield this.getNeuronCompilerVersion(`"${modelPack.compilerLog}"`)
286
+ : undefined;
287
+ if (this.options.compatibilityCheckEnabled) {
288
+ compatibilityStatus = yield this.checkCompilerVersionCompatibility(compilerVersion);
289
+ }
259
290
  const fileContent = template({
260
291
  runtime: modelPack.js.replace(".js", ""),
261
292
  model: modelPack.wasm,
@@ -263,8 +294,8 @@ class WasmRunner {
263
294
  formspec,
264
295
  modelName,
265
296
  modules: {
266
- externalResolver: externalResolverPath,
267
297
  logger: loggerPath,
298
+ workerThread: workerThread,
268
299
  },
269
300
  });
270
301
  const workerPath = path_1.default.join(tempFolder, `worker-${id}.js`);
@@ -274,26 +305,88 @@ class WasmRunner {
274
305
  EventType: "Runner.CreateTemplates",
275
306
  ModelId: id,
276
307
  });
277
- const instancePool = new node_worker_threads_pool_1.StaticPool({
278
- size: size || 1,
279
- task: workerPath,
308
+ let workerPool;
309
+ /**
310
+ * Processes the requests from Worker thread. Interprets the request
311
+ * `type` and implement necessary logic and returns the result.
312
+ *
313
+ * @param {Object} requestData
314
+ * Event details from Worker thread.
315
+ */
316
+ const processWorkerThreadRequest = (requestData) => __awaiter(this, void 0, void 0, function* () {
317
+ const { type, payload, context, threadId } = requestData;
318
+ // Worker request of model execution on main thread.
319
+ if (type === constants_1.THREAD_EVENT_TYPES.REQUEST_EXECUTE) {
320
+ try {
321
+ // Callback the resolver function
322
+ const result = yield this.externalResolverModule.sparkService(payload, context);
323
+ // Send back the response to worker thread
324
+ workerPool.postMessage({
325
+ payload: result,
326
+ type: constants_1.THREAD_EVENT_TYPES.RESPONSE,
327
+ threadId,
328
+ });
329
+ }
330
+ catch (e) {
331
+ // Send back the error to worker thread
332
+ workerPool.postMessage({
333
+ payload: e,
334
+ type: constants_1.THREAD_EVENT_TYPES.ERROR,
335
+ threadId,
336
+ });
337
+ }
338
+ }
339
+ });
340
+ const workerName = path_1.default.basename(modelPack.js, path_1.default.extname(modelPack.js)) || "";
341
+ workerPool = new workerPool_1.default(workerSize, workerPath, {
342
+ errorHandler: (e) => logger_1.default.error("Worker Error: ", e),
343
+ onlineHandler: () => logger_1.default.info(`Worker ${id} online`),
344
+ // Receive messages from worker here
345
+ messageHandler: processWorkerThreadRequest,
346
+ workerOptions: {
347
+ name: workerName,
348
+ },
349
+ enableTasksQueue: true
280
350
  });
281
351
  logger_1.default.debug({
282
352
  TimeStamp: Date.now(),
283
353
  EventType: "Runner.ModelInitialize",
284
354
  ModelId: id,
285
355
  });
356
+ workerPool.start();
286
357
  return new Promise((resolve, reject) => {
287
358
  let limit = 0;
359
+ let readyCount = workerSize;
360
+ const stats = {};
288
361
  const checkModelStatus = () => __awaiter(this, void 0, void 0, function* () {
289
- const isReady = yield instancePool.exec("isReady");
290
- if (isReady) {
362
+ var _a;
363
+ const readyRes = yield workerPool.execute("isReady");
364
+ if (readyRes && !stats[readyRes.threadId]) {
365
+ stats[readyRes.threadId] = {
366
+ init_memory: readyRes.memoryUsage,
367
+ init_time_ms: readyRes.time,
368
+ ready_ts: ((_a = readyRes.payload) === null || _a === void 0 ? void 0 : _a.readyTs) || readyRes.readyTs,
369
+ peak_memory: readyRes.memoryUsage,
370
+ last_execute_consume_memory: readyRes.memoryUsage
371
+ };
372
+ readyCount = readyCount - 1;
291
373
  logger_1.default.debug({
292
374
  TimeStamp: Date.now(),
293
375
  EventType: "Runner.ModelInitialize.Completed",
294
376
  ModelId: id,
295
377
  });
296
- resolve(instancePool);
378
+ if (readyCount === 0) {
379
+ resolve({
380
+ instance: workerPool,
381
+ compilerVersion,
382
+ compatibilityStatus,
383
+ size: workerSize,
384
+ stats: stats
385
+ });
386
+ }
387
+ else {
388
+ setTimeout(checkModelStatus, 1);
389
+ }
297
390
  }
298
391
  else if (limit >= 2000) {
299
392
  // 1 second
@@ -313,15 +406,23 @@ class WasmRunner {
313
406
  _initializeModelInstance(m) {
314
407
  return __awaiter(this, void 0, void 0, function* () {
315
408
  if (utils.isHttpURL(m.url)) {
316
- const instance = yield this._createModelInstanceFromURL(m.id, m.url, this._tempWorkerFolder.name, this._tempModelFolder.name, m.size);
409
+ const { instance, compilerVersion, compatibilityStatus, stats } = yield this._createModelInstanceFromURL(m.id, m.url, this._tempWorkerFolder.name, this._tempModelFolder.name, m.size);
317
410
  m.ready = true;
318
411
  m.worker = instance;
412
+ m.compatibility_status = compatibilityStatus;
413
+ m.compilerVersion = compilerVersion;
414
+ m.stats = stats;
415
+ m.responseTimeMetrics = new responseTimeMetric_1.default();
319
416
  return Promise.resolve();
320
417
  }
321
418
  if (yield utils.isPath(m.url)) {
322
- const instance = yield this._createModelInstance(m.id, m.url, this._tempWorkerFolder.name, m.size);
419
+ const { instance, compilerVersion, compatibilityStatus, stats } = yield this._createModelInstance(m.id, m.url, this._tempWorkerFolder.name, m.size);
323
420
  m.ready = true;
324
421
  m.worker = instance;
422
+ m.compatibility_status = compatibilityStatus;
423
+ m.compilerVersion = compilerVersion;
424
+ m.stats = stats;
425
+ m.responseTimeMetrics = new responseTimeMetric_1.default();
325
426
  return Promise.resolve();
326
427
  }
327
428
  if (Buffer.isBuffer(m.url)) {
@@ -329,9 +430,13 @@ class WasmRunner {
329
430
  const modelPath = path_1.default.join(this._tempWorkerFolder.name, `${m.id}.zip`);
330
431
  const fileWriterStream = (0, fs_1.createWriteStream)(modelPath);
331
432
  yield pipeline(model, fileWriterStream);
332
- const instance = yield this._createModelInstance(m.id, modelPath, this._tempWorkerFolder.name, m.size);
433
+ const { instance, compilerVersion, compatibilityStatus, stats } = yield this._createModelInstance(m.id, modelPath, this._tempWorkerFolder.name, m.size);
333
434
  m.ready = true;
334
435
  m.worker = instance;
436
+ m.compatibility_status = compatibilityStatus;
437
+ m.compilerVersion = compilerVersion;
438
+ m.stats = stats;
439
+ m.responseTimeMetrics = new responseTimeMetric_1.default();
335
440
  return Promise.resolve();
336
441
  }
337
442
  });
@@ -353,35 +458,70 @@ class WasmRunner {
353
458
  });
354
459
  }
355
460
  /**
356
- * Append and initialized new model to runner
461
+ * Append and initialize model in preparation for the `execute()`.
462
+ *
357
463
  * @param {ModelConfig} modelConfig
358
- * @returns Promise
464
+ * Model details like version_id as `id` and zip path as `url`.
465
+ *
466
+ * @returns {Promise<void>}
359
467
  */
360
468
  append(modelConfig) {
361
469
  return __awaiter(this, void 0, void 0, function* () {
362
- if (this.isExist(modelConfig.id)) {
363
- return;
470
+ if (this.options.compatibilityCheckEnabled) {
471
+ this.safeCompiler = yield this.getSafeCompilerVersion(this.options.runnerVersion);
472
+ }
473
+ if (!this.isExist(modelConfig.id)) {
474
+ const model = {
475
+ id: modelConfig.id,
476
+ url: modelConfig.url,
477
+ size: modelConfig.size,
478
+ busy: 0,
479
+ };
480
+ if (!this.isExist(modelConfig.id)) {
481
+ if (this.initializingModels.findIndex((im) => im === modelConfig.id) ===
482
+ -1) {
483
+ try {
484
+ this.initializingModels.push(modelConfig.id);
485
+ yield this._initializeModelInstance(model);
486
+ this.models.push(model);
487
+ }
488
+ catch (e) {
489
+ throw e;
490
+ }
491
+ finally {
492
+ this.initializingModels = this.initializingModels.filter((im) => im !== modelConfig.id);
493
+ }
494
+ }
495
+ else {
496
+ const maxRetry = 600;
497
+ let retry = 0;
498
+ while (this.initializingModels.findIndex((im) => im === modelConfig.id) >
499
+ -1 &&
500
+ retry < maxRetry) {
501
+ yield delay(100);
502
+ retry++;
503
+ }
504
+ if (retry >= maxRetry || !this.isExist(modelConfig.id)) {
505
+ throw new error_1.default.ModelInitializationError(modelConfig.id);
506
+ }
507
+ }
508
+ }
364
509
  }
365
- const model = {
366
- id: modelConfig.id,
367
- url: modelConfig.url,
368
- size: modelConfig.size,
369
- busy: 0,
370
- };
371
- yield this._initializeModelInstance(model);
372
- this.models.push(model);
373
510
  });
374
511
  }
375
512
  /**
376
- * Remove model from runner
377
- * @param {string} id Model id
378
- * @returns Promise
513
+ * Remove model from the list of initialized model useful for GC.
514
+ *
515
+ * @param {String} id
516
+ * ID of the model that needs to be removed.
517
+ *
518
+ * @returns {Promise<void>}
379
519
  */
380
520
  remove(id) {
381
521
  return __awaiter(this, void 0, void 0, function* () {
382
522
  if (this.isExist(id)) {
383
523
  const model = this.models.find((m) => m.id === id);
384
- yield model.worker.exec("destroy");
524
+ yield model.worker.execute("destroy");
385
525
  model.worker.destroy();
386
526
  delete model.worker;
387
527
  this.models = this.models.filter((m) => m.id !== id);
@@ -442,10 +582,21 @@ class WasmRunner {
442
582
  throw new error_1.default.ModelInitializationError(id);
443
583
  return model;
444
584
  }
585
+ getModelSize(id) {
586
+ const model = this._getModel(id);
587
+ return model.size || 1;
588
+ }
589
+ getModelCompilerVersion(id) {
590
+ const model = this._getModel(id);
591
+ if (model.compilerVersion && model.compilerVersion !== 'undefined') {
592
+ return model.compilerVersion;
593
+ }
594
+ return undefined;
595
+ }
445
596
  /**
446
597
  * Check if model existed
447
598
  * @param {string} id Model id
448
- * @returns boolean
599
+ * @returns {boolean}
449
600
  */
450
601
  isExist(id) {
451
602
  try {
@@ -455,15 +606,65 @@ class WasmRunner {
455
606
  return false;
456
607
  }
457
608
  }
609
+ isModelCompatible(model, input) {
610
+ if (!this.options.compatibilityCheckEnabled) {
611
+ return true;
612
+ }
613
+ switch (model.compatibility_status) {
614
+ case types_1.Compatibility.INCOMPATIBLE:
615
+ throw new error_1.default.ModelExecuteError(this.getIncompatibleMessage(model), model.id, input);
616
+ case types_1.Compatibility.PARTIAL:
617
+ logger_1.default.warn({
618
+ TimeStamp: Date.now(),
619
+ EventType: "Runner.Execute.Compatibility.Warning",
620
+ TextMessage: this.getPartiallyCompatibleMessage(model),
621
+ ModelId: model.id,
622
+ });
623
+ return true;
624
+ case types_1.Compatibility.COMPATIBLE:
625
+ return true;
626
+ }
627
+ return true;
628
+ }
629
+ getModelsStats() {
630
+ return this.models.map(model => {
631
+ const stats = Object.assign({}, model.stats);
632
+ Object.keys(stats).forEach(threadId => {
633
+ stats[threadId] = Object.assign({}, stats[threadId]);
634
+ Object.keys(stats[threadId]).forEach(k => {
635
+ if (k.indexOf('_memory') > -1) {
636
+ stats[threadId][`${k}_mb`] = utils.formatMemory(stats[threadId][k]);
637
+ delete stats[threadId][k];
638
+ }
639
+ if (k.indexOf('ready_ts') > -1) {
640
+ stats[threadId].uptime_ms = Date.now() - stats[threadId]['ready_ts'];
641
+ delete stats[threadId][k];
642
+ }
643
+ });
644
+ });
645
+ return {
646
+ stats: Object.assign(Object.assign({}, stats), { uptime_ms: Math.min(...Object.keys(stats).map((k) => stats[k].uptime_ms)), min_time_ms: model.responseTimeMetrics.min, mean_time_ms: model.responseTimeMetrics.mean, p95_time_ms: model.responseTimeMetrics.p95, p99_time_ms: model.responseTimeMetrics.p99, max_time_ms: model.responseTimeMetrics.max }),
647
+ busy: model.busy,
648
+ size: model.size,
649
+ id: model.id
650
+ };
651
+ });
652
+ }
653
+ getPartiallyCompatibleMessage(model) {
654
+ return `The Wasm was compiled using ${model.compilerVersion} which is not fully supported by this runner version ${this.safeCompiler}. The service should be able to run but could encounter errors`;
655
+ }
656
+ getIncompatibleMessage(model) {
657
+ return `The Wasm was compiled using ${model.compilerVersion} which is not supported by this runner version ${this.safeCompiler}.`;
658
+ }
458
659
  /**
459
660
  * Perform calculation. The runner will use request_meta.version_uuid of input argument to locate model
460
661
  * @async
461
662
  * @param {InputObject} input - Input data for calculation
462
663
  * @returns {Promise<ResponseObject>} - Response model data
463
664
  */
464
- execute(input, id = undefined, raw = false, cancelToken) {
465
- var _a, _b;
466
- return __awaiter(this, void 0, void 0, function* () {
665
+ execute(input_1) {
666
+ return __awaiter(this, arguments, void 0, function* (input, id = undefined, raw = false, cancelToken) {
667
+ var _a, _b, _c;
467
668
  let logInput;
468
669
  const token = cancelToken ? cancelToken.getToken() : undefined;
469
670
  if (utils.isV3Input(input)) {
@@ -486,7 +687,10 @@ class WasmRunner {
486
687
  runnerCallId: callid,
487
688
  ModelId: id,
488
689
  });
489
- const version_uuid = id || ((_b = input.request_meta) === null || _b === void 0 ? void 0 : _b.version_uuid) || (input === null || input === void 0 ? void 0 : input.version_id);
690
+ const version_uuid = id ||
691
+ ((_b = input === null || input === void 0 ? void 0 : input.request_meta) === null || _b === void 0 ? void 0 : _b.version_uuid) ||
692
+ ((_c = input === null || input === void 0 ? void 0 : input.request_meta) === null || _c === void 0 ? void 0 : _c.version_id) ||
693
+ (input === null || input === void 0 ? void 0 : input.version_id);
490
694
  if (input) {
491
695
  if (raw) {
492
696
  if (!input.request_data) {
@@ -518,15 +722,31 @@ class WasmRunner {
518
722
  model.busy = model.busy + 1;
519
723
  }
520
724
  let parsedResult;
521
- parsedResult = yield model.worker.exec(input);
522
- logger_1.default.debug({
523
- TimeStamp: Date.now(),
524
- EventType: "Runner.Execute.Completed",
525
- executeDuration: Date.now() - start,
526
- runnerCallId: callid,
527
- ModelId: id,
528
- });
529
- return parsedResult;
725
+ if (this.isModelCompatible(model, input)) {
726
+ const start = Date.now();
727
+ const res = yield model.worker.execute(input);
728
+ const execTime = Date.now() - start;
729
+ if (res.memoryUsage > model.stats[res.threadId].peak_memory) {
730
+ model.stats[res.threadId].peak_memory = res.memoryUsage;
731
+ }
732
+ model.stats[res.threadId].last_execute_consume_memory = res.memoryUsage;
733
+ model.responseTimeMetrics.record(execTime);
734
+ if (res.error) {
735
+ throw res.error;
736
+ }
737
+ parsedResult = res.payload;
738
+ logger_1.default.debug({
739
+ TimeStamp: Date.now(),
740
+ EventType: "Runner.Execute.Completed",
741
+ executeDuration: Date.now() - start,
742
+ runnerCallId: callid,
743
+ ModelId: id,
744
+ });
745
+ return parsedResult;
746
+ }
747
+ else {
748
+ throw new error_1.default.ModelExecuteError(this.getIncompatibleMessage(model), version_uuid, input);
749
+ }
530
750
  }
531
751
  catch (err) {
532
752
  logger_1.default.error(err);
@@ -547,6 +767,58 @@ class WasmRunner {
547
767
  }
548
768
  });
549
769
  }
770
+ getNeuronCompilerVersion(logFile) {
771
+ return __awaiter(this, void 0, void 0, function* () {
772
+ let compilerVersion = undefined;
773
+ const data = fs_1.default.readFileSync(logFile.replace(/"/g, ""), "utf8");
774
+ const modelJsonInfo = JSON.parse(data);
775
+ if (!semver_1.default.valid(modelJsonInfo.CompilerVersion)) {
776
+ throw new error_1.default.CompatibilityJsonError(`The Wasm was compiled using ${modelJsonInfo.CompilerVersion} which is not supported by the compatibility check.`);
777
+ }
778
+ compilerVersion = semver_1.default.coerce(modelJsonInfo.CompilerVersion).version;
779
+ return compilerVersion;
780
+ });
781
+ }
782
+ checkCompilerVersionCompatibility(modelCompilerVersion) {
783
+ return __awaiter(this, void 0, void 0, function* () {
784
+ try {
785
+ const safeCompilerVersion = this.safeCompiler;
786
+ if (modelCompilerVersion) {
787
+ if (semver_1.default.lte(modelCompilerVersion, safeCompilerVersion)) {
788
+ return types_1.Compatibility.COMPATIBLE;
789
+ }
790
+ else {
791
+ return types_1.Compatibility.PARTIAL;
792
+ }
793
+ }
794
+ else {
795
+ return types_1.Compatibility.COMPATIBLE;
796
+ }
797
+ }
798
+ catch (err) {
799
+ if (err.code === "ENOENT" || err.message === "File not found") {
800
+ return types_1.Compatibility.COMPATIBLE;
801
+ }
802
+ else if (err instanceof SyntaxError) {
803
+ return types_1.Compatibility.INCOMPATIBLE;
804
+ }
805
+ else {
806
+ throw err;
807
+ }
808
+ }
809
+ });
810
+ }
811
+ getSafeCompilerVersion(runnerVersion) {
812
+ var _a;
813
+ const runnerVersions = (_a = this.options.compatibilityList) === null || _a === void 0 ? void 0 : _a.runner_versions;
814
+ if (!this.options.compatibilityList) {
815
+ throw new error_1.default.CompatibilityJsonError("No runner compiler compatibility list provided");
816
+ }
817
+ if (!runnerVersions.hasOwnProperty(runnerVersion)) {
818
+ throw new error_1.default.CompatibilityJsonError(`No compiler compatibility found for the runner version ${runnerVersion}`);
819
+ }
820
+ return runnerVersions[runnerVersion].safe_compiler;
821
+ }
550
822
  }
551
823
  exports.WasmRunner = WasmRunner;
552
824
  //# sourceMappingURL=node.js.map