@coherentglobal/wasm-runner 0.1.4 → 0.2.4

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 (55) hide show
  1. package/README.md +137 -222
  2. package/dist/CancellationToken.js +2 -0
  3. package/dist/CancellationToken.js.map +1 -1
  4. package/dist/browser/logger.js +6 -2
  5. package/dist/browser/logger.js.map +1 -1
  6. package/dist/browser/template/main.template.js +1 -1
  7. package/dist/browser/template/main.template.js.map +1 -1
  8. package/dist/browser/template/worker.template.js +117 -108
  9. package/dist/browser/template/worker.template.js.map +1 -1
  10. package/dist/browser/template.js +2 -2
  11. package/dist/browser/template.js.map +1 -1
  12. package/dist/browser.d.ts +5 -5
  13. package/dist/browser.js +242 -257
  14. package/dist/browser.js.map +1 -1
  15. package/dist/constants.d.ts +1 -0
  16. package/dist/constants.js +2 -0
  17. package/dist/constants.js.map +1 -1
  18. package/dist/defaultExternalResolver.js +6 -15
  19. package/dist/defaultExternalResolver.js.map +1 -1
  20. package/dist/error.d.ts +5 -0
  21. package/dist/error.js +23 -5
  22. package/dist/error.js.map +1 -1
  23. package/dist/node/logger.d.ts +2 -1
  24. package/dist/node/logger.js +2 -2
  25. package/dist/node/logger.js.map +1 -1
  26. package/dist/node/logger.ts +2 -2
  27. package/dist/node/mockLogger.d.ts +2 -1
  28. package/dist/node/template/main.template.ejs +108 -69
  29. package/dist/node/threads/mockWorkerThread.d.ts +1 -0
  30. package/dist/node/threads/mockWorkerThread.js +10 -3
  31. package/dist/node/threads/mockWorkerThread.js.map +1 -1
  32. package/dist/node/threads/workerPool.d.ts +4 -5
  33. package/dist/node/threads/workerPool.js +8 -11
  34. package/dist/node/threads/workerPool.js.map +1 -1
  35. package/dist/node/threads/workerPool.ts +15 -10
  36. package/dist/node/threads/workerThread.d.ts +2 -2
  37. package/dist/node/threads/workerThread.js +36 -35
  38. package/dist/node/threads/workerThread.js.map +1 -1
  39. package/dist/node/threads/workerThread.ts +28 -25
  40. package/dist/node.d.ts +32 -7
  41. package/dist/node.js +602 -331
  42. package/dist/node.js.map +1 -1
  43. package/dist/responseTimeMetric.d.ts +20 -0
  44. package/dist/responseTimeMetric.js +159 -0
  45. package/dist/responseTimeMetric.js.map +1 -0
  46. package/dist/serializer/columnarSerializer.d.ts +9 -0
  47. package/dist/serializer/columnarSerializer.js +59 -30
  48. package/dist/serializer/columnarSerializer.js.map +1 -1
  49. package/dist/types.d.ts +36 -2
  50. package/dist/types.js +8 -0
  51. package/dist/types.js.map +1 -1
  52. package/dist/utils.d.ts +3 -1
  53. package/dist/utils.js +53 -59
  54. package/dist/utils.js.map +1 -1
  55. package/package.json +74 -60
package/dist/node.js CHANGED
@@ -15,35 +15,30 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
34
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
37
  };
37
38
  Object.defineProperty(exports, "__esModule", { value: true });
38
39
  exports.WasmRunner = exports.ModelExecuteCancelled = exports.CancellationToken = exports.ColumnarSerializer = void 0;
39
- /* eslint-disable consistent-return */
40
- /* eslint-disable no-shadow */
41
- /* eslint-disable camelcase */
42
- /* eslint-disable no-param-reassign */
43
- /* eslint-disable no-underscore-dangle */
44
40
  const tmp_1 = __importDefault(require("tmp"));
45
41
  const cuid2_1 = require("@paralleldrive/cuid2");
46
- const logger_1 = __importDefault(require("./node/logger"));
47
42
  const unzipper_1 = require("unzipper");
48
43
  const ejs_1 = __importDefault(require("ejs"));
49
44
  const stream_1 = __importStar(require("stream"));
@@ -51,11 +46,16 @@ const fs_1 = __importStar(require("fs"));
51
46
  const path_1 = __importDefault(require("path"));
52
47
  const util_1 = require("util");
53
48
  const got_1 = __importDefault(require("got"));
49
+ const semver_1 = __importDefault(require("semver"));
50
+ const events_1 = __importDefault(require("events"));
51
+ const node_os_1 = require("node:os");
54
52
  const workerPool_1 = __importDefault(require("./node/threads/workerPool"));
55
53
  const utils = __importStar(require("./utils"));
54
+ const types_1 = require("./types");
56
55
  const error_1 = __importDefault(require("./error"));
57
- const node_os_1 = require("node:os");
58
56
  const constants_1 = require("./constants");
57
+ const responseTimeMetric_1 = __importDefault(require("./responseTimeMetric"));
58
+ const logger_1 = __importDefault(require("./node/logger"));
59
59
  /* istanbul ignore next */
60
60
  var columnarSerializer_1 = require("./serializer/columnarSerializer");
61
61
  Object.defineProperty(exports, "ColumnarSerializer", { enumerable: true, get: function () { return columnarSerializer_1.ColumnarSerializer; } });
@@ -63,17 +63,16 @@ var CancellationToken_1 = require("./CancellationToken");
63
63
  Object.defineProperty(exports, "CancellationToken", { enumerable: true, get: function () { return CancellationToken_1.CancellationToken; } });
64
64
  var error_2 = require("./error");
65
65
  Object.defineProperty(exports, "ModelExecuteCancelled", { enumerable: true, get: function () { return error_2.ModelExecuteCancelled; } });
66
+ tmp_1.default.setGracefulCleanup();
66
67
  const platformUsed = (0, node_os_1.platform)();
67
68
  const pipeline = (0, util_1.promisify)(stream_1.default.pipeline);
68
- function replaceRegexInFile(file, search, replace) {
69
- return __awaiter(this, void 0, void 0, function* () {
70
- let contents = yield fs_1.default.promises.readFile(file, "utf8");
71
- let replaced_contents = contents.replace(search, replace);
72
- let tmpfile = `${file}-${(0, cuid2_1.createId)()}.jstmpreplace`;
73
- yield fs_1.default.promises.writeFile(tmpfile, replaced_contents, "utf8");
74
- yield fs_1.default.promises.rename(tmpfile, file);
75
- return true;
76
- });
69
+ async function replaceRegexInFile(file, search, replace) {
70
+ const contents = await fs_1.default.promises.readFile(file, "utf8");
71
+ const replaced_contents = contents.replace(search, replace);
72
+ const tmpfile = `${file}-${(0, cuid2_1.createId)()}.jstmpreplace`;
73
+ await fs_1.default.promises.writeFile(tmpfile, replaced_contents, "utf8");
74
+ await fs_1.default.promises.rename(tmpfile, file);
75
+ return true;
77
76
  }
78
77
  /* istanbul ignore next */
79
78
  const toWindowsPath = (fileLocation) => fileLocation.split(path_1.default.sep).join("//");
@@ -93,7 +92,7 @@ const unzipFiles = (id, url, workerFolder) => {
93
92
  let data;
94
93
  let metadata;
95
94
  let formspec;
96
- // @ts-ignore
95
+ let compilerLog;
97
96
  const stream = fs_1.default.createReadStream(url).pipe((0, unzipper_1.Parse)());
98
97
  return new Promise((resolve, reject) => {
99
98
  stream.on("entry", (entry) => {
@@ -116,6 +115,9 @@ const unzipFiles = (id, url, workerFolder) => {
116
115
  if (entry.path.toLowerCase().indexOf("_jsonformspec.json") > -1) {
117
116
  formspec = fileName;
118
117
  }
118
+ if (entry.path.toLowerCase().indexOf("_compilerlog.json") > -1) {
119
+ compilerLog = fileName;
120
+ }
119
121
  return entry.pipe(writeStream);
120
122
  }
121
123
  return entry.autodrain();
@@ -132,6 +134,7 @@ const unzipFiles = (id, url, workerFolder) => {
132
134
  data,
133
135
  metadata,
134
136
  formspec,
137
+ compilerLog,
135
138
  });
136
139
  });
137
140
  stream.on("error", (error) => reject(error));
@@ -141,41 +144,59 @@ const template = ejs_1.default.compile(fs_1.default.readFileSync(path_1.default.
141
144
  encoding: "utf8",
142
145
  flag: "r",
143
146
  }), { async: false });
144
- const delay = (time) => {
145
- return new Promise((res) => {
146
- setTimeout(res, time);
147
- });
148
- };
147
+ const delay = (time) => new Promise((res) => {
148
+ setTimeout(res, time);
149
+ });
149
150
  /**
150
151
  * WasmRunner class, responsible for managing model and it's lifecycle
151
152
  *
152
153
  * @class
153
154
  * @classdesc WasmRunner class, responsible for managing model and it's lifecycle
154
155
  */
155
- // eslint-disable-next-line import/prefer-default-export
156
- class WasmRunner {
156
+ class WasmRunner extends events_1.default {
157
+ _tempWorkerFolder;
158
+ _tempModelFolder;
159
+ license;
160
+ models;
161
+ externalResolverModule;
162
+ initializingModels;
163
+ options;
164
+ safeCompiler;
165
+ logger;
157
166
  /**
158
167
  * Create new Wasm Runner instance
159
168
  *
160
169
  * @param {RunnerConfig} wasmRunnerConfig
161
170
  */
162
- constructor(wasmRunnerConfig, externalResolverModule = "", license = "") {
171
+ constructor(wasmRunnerConfig, externalResolverModule = "", options = {}, license = "") {
172
+ super();
173
+ let baseTmpDir = null;
174
+ if (process.env.DEV_DEBUG_TMP_PATH) {
175
+ baseTmpDir = path_1.default.join(process.cwd(), process.env.DEV_DEBUG_TMP_PATH);
176
+ }
177
+ if (options.tmpDir) {
178
+ baseTmpDir = options.tmpDir;
179
+ }
163
180
  this._tempWorkerFolder = tmp_1.default.dirSync({
164
181
  prefix: "wasmserver_worker",
165
- // @ts-ignore
166
182
  mode: 0o750,
183
+ tmpdir: baseTmpDir,
167
184
  });
168
185
  this._tempModelFolder = tmp_1.default.dirSync({
169
186
  prefix: "wasmserver_model",
170
- // @ts-ignore
171
187
  mode: 0o750,
188
+ tmpdir: baseTmpDir,
172
189
  });
173
190
  this.license = license;
174
191
  this.initializingModels = [];
192
+ /* eslint-disable @typescript-eslint/no-require-imports */
175
193
  this.externalResolverModule = externalResolverModule
176
- ? require.resolve(externalResolverModule)
177
- : require.resolve("./defaultExternalResolver");
194
+ ? require(useCorrectPath(require.resolve(externalResolverModule)))
195
+ : require(require.resolve("./defaultExternalResolver"));
196
+ /* eslint-enable @typescript-eslint/no-require-imports */
178
197
  this.models = [];
198
+ this.options = options;
199
+ this.logger = this.options.logger || logger_1.default;
179
200
  if (!utils.isEmpty(wasmRunnerConfig)) {
180
201
  if (Array.isArray(wasmRunnerConfig)) {
181
202
  this.models = wasmRunnerConfig.map((wsconfig) => ({
@@ -200,190 +221,292 @@ class WasmRunner {
200
221
  * @function initialize
201
222
  */
202
223
  /* istanbul ignore next */
203
- initialize(license = "") {
204
- return __awaiter(this, void 0, void 0, function* () {
205
- yield Promise.all(this.models.map((m) => __awaiter(this, void 0, void 0, function* () {
206
- return this._initializeModelInstance(m);
207
- })));
208
- });
224
+ async initialize(license = "") {
225
+ if (this.options.compatibilityCheckEnabled) {
226
+ this.safeCompiler = await this.getSafeCompilerVersion(this.options.runnerVersion);
227
+ }
228
+ await Promise.all(this.models.map(async (m) => this._initializeModelInstance(m)));
209
229
  }
210
230
  /* istanbul ignore next */
211
- _createModelInstance(id, url, workerFolder, size) {
212
- return __awaiter(this, void 0, void 0, function* () {
213
- logger_1.default.debug({
214
- TimeStamp: Date.now(),
215
- EventType: "Runner._createModelInstance",
216
- size: size,
217
- ModelId: id,
218
- });
219
- const tempFolder = path_1.default.join(workerFolder, `${id}-${Date.now()}`);
220
- try {
221
- yield fs_1.default.promises.mkdir(tempFolder, { mode: 0o750 });
222
- }
223
- catch (err) {
224
- logger_1.default.info(`${tempFolder} existed`);
225
- }
226
- const modelPack = yield unzipFiles(id, url, tempFolder);
227
- logger_1.default.debug({
228
- TimeStamp: Date.now(),
229
- EventType: "Runner.UnZipFiles",
230
- ModelId: id,
231
- });
232
- const modelName = id;
233
- yield replaceRegexInFile(modelPack.js, `wasmBinaryFile="${path_1.default.basename(modelPack.wasm)}"`, `scriptDirectory="";wasmBinaryFile="${modelPack.wasm}"`);
234
- if (modelPack.data) {
235
- yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
236
- yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
237
- yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
238
- yield replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
239
- }
240
- // Prepare File Paths
241
- const externalResolverPath = useCorrectPath(this.externalResolverModule);
242
- const loggerPath = useCorrectPath(process.env.NODE_ENV === "test"
243
- ? require.resolve("./node/mockLogger.js")
244
- : require.resolve("./node/logger"));
245
- const workerThread = useCorrectPath(process.env.NODE_ENV === "test"
246
- ? require.resolve("./node/threads/mockWorkerThread.js")
247
- : require.resolve("./node/threads/workerThread"));
248
- let metadata = "undefined";
249
- if (!utils.isEmpty(modelPack.metadata)) {
250
- const metadataPath = useCorrectPath(require.resolve(modelPack.metadata));
251
- metadata = `require("${metadataPath}")`;
252
- }
253
- let formspec = "undefined";
254
- if (!utils.isEmpty(modelPack.formspec)) {
255
- const formSpecPath = useCorrectPath(require.resolve(modelPack.formspec));
256
- formspec = `require("${formSpecPath}")`;
231
+ async _createModelInstance(id, url, workerFolder, size = 1) {
232
+ const workerSize = size || 1;
233
+ this.logger.debug({
234
+ TimeStamp: Date.now(),
235
+ EventType: "Runner._createModelInstance",
236
+ size: workerSize,
237
+ ModelId: id,
238
+ });
239
+ const tempFolder = path_1.default.join(workerFolder, `${id}-${Date.now()}`);
240
+ try {
241
+ await fs_1.default.promises.mkdir(tempFolder, { mode: 0o750 });
242
+ }
243
+ catch (_err) {
244
+ this.logger.info(`${tempFolder} existed`);
245
+ }
246
+ const modelPack = await unzipFiles(id, url, tempFolder);
247
+ this.logger.debug({
248
+ TimeStamp: Date.now(),
249
+ EventType: "Runner.UnZipFiles",
250
+ ModelId: id,
251
+ });
252
+ const modelName = id;
253
+ await replaceRegexInFile(modelPack.js, `wasmBinaryFile="${path_1.default.basename(modelPack.wasm)}"`, `scriptDirectory="";wasmBinaryFile="${modelPack.wasm}"`);
254
+ const replaceIsFileURLFunc = "function isFileURI(filename) { if(typeof process !== undefined && process.versions && process.versions.node) { return true; }";
255
+ await replaceRegexInFile(modelPack.js, "function isFileURI(filename) {", replaceIsFileURLFunc);
256
+ await replaceRegexInFile(modelPack.js, "function isFileURI(filename){", replaceIsFileURLFunc);
257
+ // Remove `new URL()` since `isFileURI` will always be true for Node
258
+ await replaceRegexInFile(modelPack.js, /\s*isFileURI\s*\(\s*filename\s*\)\s*\?\s*new\s*URL\s*\(\s*filename\s*\)\s*:\s*/g, "");
259
+ if (modelPack.data) {
260
+ await replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
261
+ await replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
262
+ await replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
263
+ await replaceRegexInFile(modelPack.js, `"${path_1.default.basename(modelPack.data)}"`, `"${modelPack.data}"`);
264
+ }
265
+ // Prepare File Paths
266
+ // const externalResolverPath = useCorrectPath(this.externalResolverModule);
267
+ const loggerPath = useCorrectPath(process.env.NODE_ENV === "test"
268
+ ? require.resolve("./node/mockLogger.js")
269
+ : require.resolve("./node/logger"));
270
+ const workerThread = useCorrectPath(process.env.NODE_ENV === "test"
271
+ ? require.resolve("./node/threads/mockWorkerThread.js")
272
+ : require.resolve("./node/threads/workerThread"));
273
+ let metadata = "undefined";
274
+ if (!utils.isEmpty(modelPack.metadata)) {
275
+ const metadataPath = useCorrectPath(require.resolve(modelPack.metadata));
276
+ metadata = `require("${metadataPath}")`;
277
+ }
278
+ let formspec = "undefined";
279
+ if (!utils.isEmpty(modelPack.formspec)) {
280
+ const formSpecPath = useCorrectPath(require.resolve(modelPack.formspec));
281
+ formspec = `require("${formSpecPath}")`;
282
+ }
283
+ let compilerVersion = "undefined";
284
+ let compatibilityStatus = "undefined";
285
+ compilerVersion = !utils.isEmpty(modelPack.compilerLog)
286
+ ? await this.getNeuronCompilerVersion(`"${modelPack.compilerLog}"`)
287
+ : undefined;
288
+ if (this.options.compatibilityCheckEnabled) {
289
+ compatibilityStatus =
290
+ await this.checkCompilerVersionCompatibility(compilerVersion);
291
+ }
292
+ const fileContent = template({
293
+ runtime: modelPack.js.replace(".js", ""),
294
+ model: modelPack.wasm,
295
+ metadata,
296
+ formspec,
297
+ modelName,
298
+ modules: {
299
+ logger: loggerPath,
300
+ workerThread: workerThread,
301
+ },
302
+ });
303
+ const workerPath = path_1.default.join(tempFolder, `worker-${id}.js`);
304
+ await fs_1.default.promises.writeFile(workerPath, fileContent);
305
+ this.logger.debug({
306
+ TimeStamp: Date.now(),
307
+ EventType: "Runner.CreateTemplates",
308
+ ModelId: id,
309
+ });
310
+ let workerPool;
311
+ let reportedStats = 0;
312
+ /**
313
+ * Processes the requests from Worker thread. Interprets the request
314
+ * `type` and implement necessary logic and returns the result.
315
+ *
316
+ * @param {Object} requestData
317
+ * Event details from Worker thread.
318
+ */
319
+ const processWorkerThreadRequest = async (requestData) => {
320
+ const { type, payload, context, threadId } = requestData;
321
+ // Worker request of model execution on main thread.
322
+ if (type === constants_1.THREAD_EVENT_TYPES.REQUEST_EXECUTE) {
323
+ try {
324
+ // Callback the resolver function
325
+ const result = await this.externalResolverModule.sparkService(payload, context);
326
+ // Send back the response to worker thread
327
+ workerPool.postMessage({
328
+ payload: result,
329
+ type: constants_1.THREAD_EVENT_TYPES.RESPONSE,
330
+ threadId,
331
+ });
332
+ }
333
+ catch (e) {
334
+ // Send back the error to worker thread
335
+ workerPool.postMessage({
336
+ payload: e,
337
+ type: constants_1.THREAD_EVENT_TYPES.ERROR,
338
+ threadId,
339
+ });
340
+ }
257
341
  }
258
- const fileContent = template({
259
- runtime: modelPack.js.replace(".js", ""),
260
- model: modelPack.wasm,
261
- metadata,
262
- formspec,
263
- modelName,
264
- modules: {
265
- externalResolver: externalResolverPath,
266
- logger: loggerPath,
267
- workerThread: workerThread,
268
- },
269
- });
270
- const workerPath = path_1.default.join(tempFolder, `worker-${id}.js`);
271
- yield fs_1.default.promises.writeFile(workerPath, fileContent);
272
- logger_1.default.debug({
273
- TimeStamp: Date.now(),
274
- EventType: "Runner.CreateTemplates",
275
- ModelId: id,
276
- });
277
- /**
278
- * Processes the requests from Worker thread. Interprets the request
279
- * `type` and implement necessary logic and returns the result.
280
- *
281
- * @param {Object} requestData
282
- * Event details from Worker thread.
283
- */
284
- const processWorkerThreadRequest = (requestData) => __awaiter(this, void 0, void 0, function* () {
285
- const { type, payload, eventId, context } = requestData;
286
- // Worker request of model execution on main thread.
287
- if (type === constants_1.THREAD_EVENT_TYPES.REQUEST_EXECUTE) {
288
- try {
289
- const resolverModule = require(this.externalResolverModule);
290
- // Callback the resolver function
291
- const result = yield resolverModule.sparkService(payload, context);
292
- // Send back the response to worker thread
293
- workerPool.postMessage({
294
- payload: result,
295
- type: constants_1.THREAD_EVENT_TYPES.RESPONSE,
296
- eventId,
297
- });
342
+ if (type === constants_1.THREAD_EVENT_TYPES.STATS_REPORT) {
343
+ const { payload, threadId } = requestData;
344
+ const worker = this.models.find((m) => m.id === workerPool.id);
345
+ if (!worker) {
346
+ // Worker has been removed and this is a stale report
347
+ return;
348
+ }
349
+ // Initialize stats object if it doesn't exist
350
+ if (!worker.stats) {
351
+ worker.stats = {};
352
+ }
353
+ // Initialize stats for threadId if it doesn't exist
354
+ if (!worker.stats[threadId]) {
355
+ worker.stats[threadId] = {
356
+ init_memory: 0,
357
+ init_time_ms: 0,
358
+ ready_ts: Date.now(),
359
+ peak_memory: 0,
360
+ current_memory: 0,
361
+ last_execute_consume_memory: 0,
362
+ };
363
+ }
364
+ if (worker && worker.stats && worker.stats[threadId]) {
365
+ worker.stats[threadId].current_memory = payload.memoryUsage;
366
+ if (payload.memoryUsage > worker.stats[threadId].peak_memory) {
367
+ worker.stats[threadId].peak_memory = payload.memoryUsage;
298
368
  }
299
- catch (e) {
300
- // Send back the error to worker thread
301
- workerPool.postMessage({
302
- payload: e,
303
- type: constants_1.THREAD_EVENT_TYPES.ERROR,
304
- eventId,
305
- });
369
+ reportedStats += 1;
370
+ if (reportedStats >= workerSize) {
371
+ reportedStats = 0;
372
+ setTimeout(() => {
373
+ if (!this.isExist(workerPool.id)) {
374
+ return;
375
+ }
376
+ this.emit("worker_report_stats", {
377
+ id: workerPool.id,
378
+ thread_stats: worker.stats,
379
+ current_memory: Object.values(worker.stats)
380
+ .map((thread) => {
381
+ if (utils.isNumber(thread.current_memory)) {
382
+ return thread.current_memory;
383
+ }
384
+ return 0;
385
+ })
386
+ .reduce((sum, memory) => sum + memory, 0),
387
+ });
388
+ }, 10);
306
389
  }
307
390
  }
308
- });
309
- const workerPool = new workerPool_1.default(size || 1, workerPath, {
310
- errorHandler: (e) => logger_1.default.error("Worker Error: ", e),
311
- // Receive messages from worker here
312
- messageHandler: processWorkerThreadRequest,
313
- });
314
- logger_1.default.debug({
315
- TimeStamp: Date.now(),
316
- EventType: "Runner.ModelInitialize",
317
- ModelId: id,
318
- });
319
- return new Promise((resolve, reject) => {
320
- let limit = 0;
321
- const checkModelStatus = () => __awaiter(this, void 0, void 0, function* () {
322
- const isReady = yield workerPool.execute("isReady");
323
- if (isReady) {
324
- logger_1.default.debug({
325
- TimeStamp: Date.now(),
326
- EventType: "Runner.ModelInitialize.Completed",
327
- ModelId: id,
391
+ }
392
+ };
393
+ const workerName = path_1.default.basename(modelPack.js, path_1.default.extname(modelPack.js)) || "";
394
+ workerPool = new workerPool_1.default(id, workerSize, workerPath, {
395
+ errorHandler: (e) => logger_1.default &&
396
+ logger_1.default.error({
397
+ EventType: "Worker.Error",
398
+ Error: e,
399
+ ModelId: id,
400
+ TextMessage: e.message,
401
+ }, "Worker Error"),
402
+ onlineHandler: () => logger_1.default &&
403
+ logger_1.default.info({ EventType: "Worker.Online", ModelId: id }, `Worker ${id} online`),
404
+ // Receive messages from worker here
405
+ messageHandler: processWorkerThreadRequest,
406
+ workerOptions: {
407
+ name: workerName,
408
+ },
409
+ });
410
+ this.logger.debug({
411
+ TimeStamp: Date.now(),
412
+ EventType: "Runner.ModelInitialize",
413
+ ModelId: id,
414
+ });
415
+ return new Promise((resolve, reject) => {
416
+ let limit = 0;
417
+ let readyCount = workerSize;
418
+ const stats = {};
419
+ const checkModelStatus = async () => {
420
+ const readyRes = await workerPool.execute("isReady");
421
+ if (readyRes && !stats[readyRes.threadId]) {
422
+ stats[readyRes.threadId] = {
423
+ init_memory: readyRes.memoryUsage,
424
+ init_time_ms: readyRes.time,
425
+ ready_ts: readyRes.payload?.readyTs || readyRes.readyTs,
426
+ peak_memory: readyRes.memoryUsage,
427
+ last_execute_consume_memory: readyRes.memoryUsage,
428
+ };
429
+ readyCount -= 1;
430
+ this.logger.debug({
431
+ TimeStamp: Date.now(),
432
+ EventType: "Runner.ModelInitialize.Completed",
433
+ ModelId: id,
434
+ });
435
+ if (readyCount === 0) {
436
+ resolve({
437
+ instance: workerPool,
438
+ compilerVersion,
439
+ compatibilityStatus,
440
+ size: workerSize,
441
+ stats: stats,
328
442
  });
329
- resolve(workerPool);
330
- }
331
- else if (limit >= 2000) {
332
- // 1 second
333
- reject(new Error("Model initialize issue"));
334
443
  }
335
444
  else {
336
- setTimeout(checkModelStatus, 15);
337
- // eslint-disable-next-line no-plusplus
338
- limit++;
445
+ setTimeout(checkModelStatus, 1);
339
446
  }
340
- });
341
- setTimeout(checkModelStatus, 15);
342
- });
447
+ }
448
+ else if (limit >= 2000) {
449
+ // 1 second
450
+ reject(new Error("Model initialize issue"));
451
+ }
452
+ else {
453
+ setTimeout(checkModelStatus, 15);
454
+ limit++;
455
+ }
456
+ };
457
+ setTimeout(checkModelStatus, 15);
343
458
  });
344
459
  }
345
460
  /* istanbul ignore next */
346
- _initializeModelInstance(m) {
347
- return __awaiter(this, void 0, void 0, function* () {
348
- if (utils.isHttpURL(m.url)) {
349
- const instance = yield this._createModelInstanceFromURL(m.id, m.url, this._tempWorkerFolder.name, this._tempModelFolder.name, m.size);
350
- m.ready = true;
351
- m.worker = instance;
352
- return Promise.resolve();
353
- }
354
- if (yield utils.isPath(m.url)) {
355
- const instance = yield this._createModelInstance(m.id, m.url, this._tempWorkerFolder.name, m.size);
356
- m.ready = true;
357
- m.worker = instance;
358
- return Promise.resolve();
359
- }
360
- if (Buffer.isBuffer(m.url)) {
361
- const model = stream_1.Readable.from(m.url);
362
- const modelPath = path_1.default.join(this._tempWorkerFolder.name, `${m.id}.zip`);
363
- const fileWriterStream = (0, fs_1.createWriteStream)(modelPath);
364
- yield pipeline(model, fileWriterStream);
365
- const instance = yield this._createModelInstance(m.id, modelPath, this._tempWorkerFolder.name, m.size);
366
- m.ready = true;
367
- m.worker = instance;
368
- return Promise.resolve();
369
- }
370
- });
461
+ async _initializeModelInstance(m) {
462
+ if (utils.isHttpURL(m.url)) {
463
+ const { instance, compilerVersion, compatibilityStatus, stats } = await this._createModelInstanceFromURL(m.id, m.url, this._tempWorkerFolder.name, this._tempModelFolder.name, m.size);
464
+ m.ready = true;
465
+ m.worker = instance;
466
+ m.compatibility_status = compatibilityStatus;
467
+ m.compilerVersion = compilerVersion;
468
+ m.stats = stats;
469
+ m.responseTimeMetrics = new responseTimeMetric_1.default();
470
+ return Promise.resolve();
471
+ }
472
+ if (await utils.isPath(m.url)) {
473
+ const { instance, compilerVersion, compatibilityStatus, stats } = await this._createModelInstance(m.id, m.url, this._tempWorkerFolder.name, m.size);
474
+ m.ready = true;
475
+ m.worker = instance;
476
+ m.compatibility_status = compatibilityStatus;
477
+ m.compilerVersion = compilerVersion;
478
+ m.stats = stats;
479
+ m.responseTimeMetrics = new responseTimeMetric_1.default();
480
+ return Promise.resolve();
481
+ }
482
+ if (Buffer.isBuffer(m.url)) {
483
+ const model = stream_1.Readable.from(m.url);
484
+ const modelPath = path_1.default.join(this._tempWorkerFolder.name, `${m.id}.zip`);
485
+ const fileWriterStream = (0, fs_1.createWriteStream)(modelPath);
486
+ await pipeline(model, fileWriterStream);
487
+ const { instance, compilerVersion, compatibilityStatus, stats } = await this._createModelInstance(m.id, modelPath, this._tempWorkerFolder.name, m.size);
488
+ m.ready = true;
489
+ m.worker = instance;
490
+ m.compatibility_status = compatibilityStatus;
491
+ m.compilerVersion = compilerVersion;
492
+ m.stats = stats;
493
+ m.responseTimeMetrics = new responseTimeMetric_1.default();
494
+ return Promise.resolve();
495
+ }
371
496
  }
372
497
  /* istanbul ignore next */
373
- _createModelInstanceFromURL(id, url, workerFolder, modelFolder, size) {
374
- return __awaiter(this, void 0, void 0, function* () {
375
- try {
376
- const modelPath = path_1.default.join(modelFolder, `${id}.zip`);
377
- const downloadStream = got_1.default.stream(url);
378
- const fileWriterStream = (0, fs_1.createWriteStream)(modelPath);
379
- yield pipeline(downloadStream, fileWriterStream);
380
- const instance = yield this._createModelInstance(id, modelPath, workerFolder, size);
381
- return instance;
382
- }
383
- catch (err) {
384
- throw new error_1.default.ModelError(`Error createModelInstanceFromURL: id:${id}, url:${url}, error:${err.message}`);
385
- }
386
- });
498
+ async _createModelInstanceFromURL(id, url, workerFolder, modelFolder, size) {
499
+ try {
500
+ const modelPath = path_1.default.join(modelFolder, `${id}.zip`);
501
+ const downloadStream = got_1.default.stream(url);
502
+ const fileWriterStream = (0, fs_1.createWriteStream)(modelPath);
503
+ await pipeline(downloadStream, fileWriterStream);
504
+ const instance = await this._createModelInstance(id, modelPath, workerFolder, size);
505
+ return instance;
506
+ }
507
+ catch (err) {
508
+ throw new error_1.default.ModelError(`Error createModelInstanceFromURL: id:${id}, url:${url}, error:${err.message}`);
509
+ }
387
510
  }
388
511
  /**
389
512
  * Append and initialize model in preparation for the `execute()`.
@@ -393,43 +516,44 @@ class WasmRunner {
393
516
  *
394
517
  * @returns {Promise<void>}
395
518
  */
396
- append(modelConfig) {
397
- return __awaiter(this, void 0, void 0, function* () {
519
+ async append(modelConfig) {
520
+ if (this.options.compatibilityCheckEnabled) {
521
+ this.safeCompiler = await this.getSafeCompilerVersion(this.options.runnerVersion);
522
+ }
523
+ if (!this.isExist(modelConfig.id)) {
524
+ const model = {
525
+ id: modelConfig.id,
526
+ url: modelConfig.url,
527
+ size: modelConfig.size,
528
+ busy: 0,
529
+ };
398
530
  if (!this.isExist(modelConfig.id)) {
399
- const model = {
400
- id: modelConfig.id,
401
- url: modelConfig.url,
402
- size: modelConfig.size,
403
- busy: 0,
404
- };
405
- if (!this.isExist(modelConfig.id)) {
406
- if (this.initializingModels.findIndex((im) => im === modelConfig.id) === -1) {
407
- try {
408
- this.initializingModels.push(modelConfig.id);
409
- yield this._initializeModelInstance(model);
410
- this.models.push(model);
411
- }
412
- catch (e) {
413
- throw e;
414
- }
415
- finally {
416
- this.initializingModels = this.initializingModels.filter((im) => im !== modelConfig.id);
417
- }
531
+ if (this.initializingModels.findIndex((im) => im === modelConfig.id) ===
532
+ -1) {
533
+ try {
534
+ this.initializingModels.push(modelConfig.id);
535
+ await this._initializeModelInstance(model);
536
+ this.models.push(model);
418
537
  }
419
- else {
420
- const maxRetry = 600;
421
- let retry = 0;
422
- while (this.initializingModels.findIndex((im) => im === modelConfig.id) > -1 && retry < maxRetry) {
423
- yield delay(100);
424
- retry++;
425
- }
426
- if (retry >= maxRetry || !this.isExist(modelConfig.id)) {
427
- throw new error_1.default.ModelInitializationError(modelConfig.id);
428
- }
538
+ finally {
539
+ this.initializingModels = this.initializingModels.filter((im) => im !== modelConfig.id);
540
+ }
541
+ }
542
+ else {
543
+ const maxRetry = 600;
544
+ let retry = 0;
545
+ while (this.initializingModels.findIndex((im) => im === modelConfig.id) >
546
+ -1 &&
547
+ retry < maxRetry) {
548
+ await delay(100);
549
+ retry++;
550
+ }
551
+ if (retry >= maxRetry || !this.isExist(modelConfig.id)) {
552
+ throw new error_1.default.ModelInitializationError(modelConfig.id);
429
553
  }
430
554
  }
431
555
  }
432
- });
556
+ }
433
557
  }
434
558
  /**
435
559
  * Remove model from the list of initialized model useful for GC.
@@ -439,21 +563,20 @@ class WasmRunner {
439
563
  *
440
564
  * @returns {Promise<void>}
441
565
  */
442
- remove(id) {
443
- return __awaiter(this, void 0, void 0, function* () {
444
- if (this.isExist(id)) {
445
- const model = this.models.find((m) => m.id === id);
446
- yield model.worker.execute("destroy");
566
+ async remove(id) {
567
+ if (this.isExist(id)) {
568
+ const model = this.models.find((m) => m.id === id);
569
+ this.models = this.models.filter((m) => m.id !== id);
570
+ setImmediate(async () => {
571
+ await model.worker.execute("destroy");
447
572
  model.worker.destroy();
448
573
  delete model.worker;
449
- this.models = this.models.filter((m) => m.id !== id);
450
- }
451
- });
574
+ });
575
+ }
452
576
  }
453
577
  /* istanbul ignore next */
454
578
  _parseError(rawResponse) {
455
- var _a;
456
- let errors = (_a = rawResponse.response_data) === null || _a === void 0 ? void 0 : _a.errors.reduce((prev, curr) => {
579
+ let errors = rawResponse.response_data?.errors.reduce((prev, curr) => {
457
580
  const { source_path } = curr;
458
581
  if (!source_path) {
459
582
  return prev;
@@ -504,6 +627,17 @@ class WasmRunner {
504
627
  throw new error_1.default.ModelInitializationError(id);
505
628
  return model;
506
629
  }
630
+ getModelSize(id) {
631
+ const model = this._getModel(id);
632
+ return model.size || 1;
633
+ }
634
+ getModelCompilerVersion(id) {
635
+ const model = this._getModel(id);
636
+ if (model.compilerVersion && model.compilerVersion !== "undefined") {
637
+ return model.compilerVersion;
638
+ }
639
+ return undefined;
640
+ }
507
641
  /**
508
642
  * Check if model existed
509
643
  * @param {string} id Model id
@@ -513,104 +647,241 @@ class WasmRunner {
513
647
  try {
514
648
  return !!this._getModel(id);
515
649
  }
516
- catch (err) {
650
+ catch (_err) {
517
651
  return false;
518
652
  }
519
653
  }
654
+ isModelCompatible(model, input) {
655
+ if (!this.options.compatibilityCheckEnabled) {
656
+ return true;
657
+ }
658
+ switch (model.compatibility_status) {
659
+ case types_1.Compatibility.INCOMPATIBLE:
660
+ throw new error_1.default.ModelExecuteError(this.getIncompatibleMessage(model), model.id, input);
661
+ case types_1.Compatibility.PARTIAL:
662
+ this.logger.warn({
663
+ TimeStamp: Date.now(),
664
+ EventType: "Runner.Execute.Compatibility.Warning",
665
+ TextMessage: this.getPartiallyCompatibleMessage(model),
666
+ ModelId: model.id,
667
+ });
668
+ return true;
669
+ case types_1.Compatibility.COMPATIBLE:
670
+ return true;
671
+ default:
672
+ return true;
673
+ }
674
+ }
675
+ getModelsStats() {
676
+ return this.models.map((model) => {
677
+ const stats = { ...model.stats };
678
+ Object.keys(stats).forEach((threadId) => {
679
+ stats[threadId] = { ...stats[threadId] };
680
+ Object.keys(stats[threadId]).forEach((k) => {
681
+ if (k.indexOf("_memory") > -1) {
682
+ stats[threadId][`${k}_mb`] = utils.formatMemory(stats[threadId][k]);
683
+ delete stats[threadId][k];
684
+ }
685
+ if (k.indexOf("ready_ts") > -1) {
686
+ stats[threadId].uptime_ms = Date.now() - stats[threadId].ready_ts;
687
+ delete stats[threadId][k];
688
+ }
689
+ });
690
+ });
691
+ return {
692
+ thread_stats: {
693
+ ...stats,
694
+ },
695
+ memory_usage_mb: Object.values(stats)
696
+ .map((thread) => thread.current_memory_mb || 0)
697
+ .reduce((sum, memory) => sum + memory, 0),
698
+ uptime_ms: Math.min(...Object.keys(stats).map((k) => stats[k].uptime_ms)),
699
+ min_time_ms: model.responseTimeMetrics.min,
700
+ mean_time_ms: model.responseTimeMetrics.mean,
701
+ p95_time_ms: model.responseTimeMetrics.p95,
702
+ p99_time_ms: model.responseTimeMetrics.p99,
703
+ max_time_ms: model.responseTimeMetrics.max,
704
+ busy: model.busy,
705
+ size: model.size,
706
+ id: model.id,
707
+ };
708
+ });
709
+ }
710
+ getPartiallyCompatibleMessage(model) {
711
+ 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`;
712
+ }
713
+ getIncompatibleMessage(model) {
714
+ return `The Wasm was compiled using ${model.compilerVersion} which is not supported by this runner version ${this.safeCompiler}.`;
715
+ }
520
716
  /**
521
717
  * Perform calculation. The runner will use request_meta.version_uuid of input argument to locate model
522
718
  * @async
523
719
  * @param {InputObject} input - Input data for calculation
524
720
  * @returns {Promise<ResponseObject>} - Response model data
525
721
  */
526
- execute(input, id = undefined, raw = false, cancelToken) {
527
- var _a, _b, _c;
528
- return __awaiter(this, void 0, void 0, function* () {
529
- let logInput;
530
- const token = cancelToken ? cancelToken.getToken() : undefined;
531
- if (utils.isV3Input(input)) {
532
- logInput = {
533
- request_data: {
534
- inputs: (_a = input === null || input === void 0 ? void 0 : input.request_data) === null || _a === void 0 ? void 0 : _a.inputs,
535
- },
536
- request_meta: Object.assign(Object.assign({}, input.request_meta), (input.request_meta._ctx ? { _ctx: "[REDACTED]" } : {})),
537
- };
538
- logger_1.default.trace(`EXECUTING: ${id}, input: ${JSON.stringify(logInput)}`);
539
- }
540
- else {
541
- logger_1.default.trace(`EXECUTING: ${id}, input: ${JSON.stringify(input)}`);
542
- }
543
- const callid = input.callid || (0, cuid2_1.createId)();
544
- const start = Date.now();
545
- logger_1.default.debug({
722
+ async execute(input, id = undefined, raw = false, cancelToken) {
723
+ let logInput;
724
+ const token = cancelToken ? cancelToken.getToken() : undefined;
725
+ if (utils.isV3Input(input)) {
726
+ logInput = {
727
+ request_data: {
728
+ inputs: input?.request_data?.inputs,
729
+ },
730
+ request_meta: {
731
+ ...input.request_meta,
732
+ ...(input.request_meta._ctx ? { _ctx: "[REDACTED]" } : {}),
733
+ },
734
+ };
735
+ this.logger.trace({
736
+ TimeStamp: Date.now(),
737
+ EventType: "Runner.Execute.V3",
738
+ TextMessage: `EXECUTING`,
739
+ JSONPayload: { input: logInput, modelId: id },
740
+ });
741
+ }
742
+ else {
743
+ this.logger.trace({
546
744
  TimeStamp: Date.now(),
547
745
  EventType: "Runner.Execute",
548
- runnerCallId: callid,
549
- ModelId: id,
746
+ TextMessage: `EXECUTING`,
747
+ JSONPayload: { input: input, modelId: id },
550
748
  });
551
- const version_uuid = id ||
552
- ((_b = input === null || input === void 0 ? void 0 : input.request_meta) === null || _b === void 0 ? void 0 : _b.version_uuid) ||
553
- ((_c = input === null || input === void 0 ? void 0 : input.request_meta) === null || _c === void 0 ? void 0 : _c.version_id) ||
554
- (input === null || input === void 0 ? void 0 : input.version_id);
555
- if (input) {
556
- if (raw) {
557
- if (!input.request_data) {
558
- input.request_data = {};
559
- }
560
- input.request_data._raw = true;
749
+ }
750
+ const callid = input.callid || (0, cuid2_1.createId)();
751
+ const version_uuid = id ||
752
+ input?.request_meta?.version_uuid ||
753
+ input?.request_meta?.version_id ||
754
+ input?.version_id;
755
+ if (input) {
756
+ if (raw) {
757
+ if (!input.request_data) {
758
+ input.request_data = {};
561
759
  }
562
- const model = this._getModel(version_uuid);
563
- try {
564
- if (!input.callid) {
565
- input.callid = callid;
566
- }
567
- if (model.busy < model.size) {
568
- model.busy = model.busy + 1;
569
- }
570
- else {
571
- while (model.busy >= model.size) {
572
- if (token) {
573
- token.throwIfCancelled(version_uuid, input);
574
- }
575
- yield new Promise((resolve) => setTimeout(resolve, 1));
576
- logger_1.default.debug({
577
- TimeStamp: Date.now(),
578
- EventType: "Runner.Execute.Waiting",
579
- runnerCallId: callid,
580
- ModelId: id,
581
- });
582
- }
583
- model.busy = model.busy + 1;
760
+ input.request_data._raw = true;
761
+ }
762
+ const model = this._getModel(version_uuid);
763
+ try {
764
+ if (!input.callid) {
765
+ input.callid = callid;
766
+ }
767
+ this.logger.debug({
768
+ TimeStamp: Date.now(),
769
+ EventType: "Runner.Execute",
770
+ TextMessage: `PREEXECUTING ${id}`,
771
+ JSONPayload: {
772
+ runnerCallId: callid,
773
+ ModelId: id,
774
+ },
775
+ });
776
+ if (this.isModelCompatible(model, input)) {
777
+ const start = Date.now();
778
+ if (token) {
779
+ token.throwIfCancelled(version_uuid, input);
584
780
  }
585
- let parsedResult;
586
- parsedResult = yield model.worker.execute(input);
587
- logger_1.default.debug({
781
+ this.logger.debug({
782
+ TimeStamp: Date.now(),
783
+ EventType: "Runner.Execute",
784
+ TextMessage: `START EXECUTING ${id}`,
785
+ JSONPayload: {
786
+ runnerCallId: callid,
787
+ ModelId: id,
788
+ },
789
+ });
790
+ const res = await model.worker.execute(input);
791
+ const execTime = Date.now() - start;
792
+ this.logger.debug({
588
793
  TimeStamp: Date.now(),
589
794
  EventType: "Runner.Execute.Completed",
590
- executeDuration: Date.now() - start,
591
- runnerCallId: callid,
592
- ModelId: id,
795
+ TextMessage: `EXECUTE COMPLETED ${id}`,
796
+ JSONPayload: {
797
+ executeDuration: execTime,
798
+ runnerCallId: callid,
799
+ ModelId: id,
800
+ },
593
801
  });
594
- return parsedResult;
595
- }
596
- catch (err) {
597
- logger_1.default.error(err);
598
- if (err.name === "validation_error") {
599
- throw err;
802
+ if (res.memoryUsage > model.stats[res.threadId].peak_memory) {
803
+ model.stats[res.threadId].peak_memory = res.memoryUsage;
600
804
  }
601
- if (err.name === "execution_cancelled") {
602
- throw err;
805
+ model.stats[res.threadId].last_execute_consume_memory =
806
+ res.memoryUsage;
807
+ model.responseTimeMetrics.record(execTime);
808
+ if (res.error) {
809
+ throw res.error;
603
810
  }
604
- throw new error_1.default.ModelExecuteError(err.message, version_uuid, input);
811
+ return res.payload;
605
812
  }
606
- finally {
607
- model.busy = model.busy - 1;
813
+ throw new error_1.default.ModelExecuteError(this.getIncompatibleMessage(model), version_uuid, input);
814
+ }
815
+ catch (err) {
816
+ this.logger.error(err);
817
+ if (err.name === "validation_error") {
818
+ throw err;
608
819
  }
820
+ if (err.name === "execution_cancelled") {
821
+ throw err;
822
+ }
823
+ throw new error_1.default.ModelExecuteError(err.message, version_uuid, input);
609
824
  }
610
- else {
611
- throw new error_1.default.ParameterRequiredError("request_meta.version_uuid", input);
825
+ }
826
+ else {
827
+ throw new error_1.default.ParameterRequiredError("request_meta.version_uuid", input);
828
+ }
829
+ }
830
+ async getNeuronCompilerVersion(logFile) {
831
+ let data;
832
+ let modelJsonInfo;
833
+ try {
834
+ data = fs_1.default.readFileSync(logFile.replace(/"/g, ""), "utf8");
835
+ modelJsonInfo = JSON.parse(data);
836
+ }
837
+ catch (err) {
838
+ this.logger.warn({
839
+ TimeStamp: Date.now(),
840
+ EventType: "Runner.GetNeuronCompilerVersion",
841
+ TextMessage: `Failed to get neuron compiler version from ${logFile}`,
842
+ JSONPayload: {
843
+ error: err.message,
844
+ logFile,
845
+ },
846
+ });
847
+ return undefined;
848
+ }
849
+ if (!semver_1.default.valid(modelJsonInfo.CompilerVersion)) {
850
+ throw new error_1.default.CompatibilityJsonError(`The Wasm was compiled using ${modelJsonInfo.CompilerVersion} which is not supported by the compatibility check.`);
851
+ }
852
+ const compilerVersion = semver_1.default.coerce(modelJsonInfo.CompilerVersion).version;
853
+ return compilerVersion;
854
+ }
855
+ async checkCompilerVersionCompatibility(modelCompilerVersion) {
856
+ try {
857
+ const safeCompilerVersion = this.safeCompiler;
858
+ if (modelCompilerVersion) {
859
+ if (semver_1.default.lte(modelCompilerVersion, safeCompilerVersion)) {
860
+ return types_1.Compatibility.COMPATIBLE;
861
+ }
862
+ return types_1.Compatibility.PARTIAL;
612
863
  }
613
- });
864
+ return types_1.Compatibility.COMPATIBLE;
865
+ }
866
+ catch (err) {
867
+ if (err.code === "ENOENT" || err.message === "File not found") {
868
+ return types_1.Compatibility.COMPATIBLE;
869
+ }
870
+ if (err instanceof SyntaxError) {
871
+ return types_1.Compatibility.INCOMPATIBLE;
872
+ }
873
+ throw err;
874
+ }
875
+ }
876
+ getSafeCompilerVersion(runnerVersion) {
877
+ const runnerVersions = this.options.compatibilityList?.runner_versions;
878
+ if (!this.options.compatibilityList) {
879
+ throw new error_1.default.CompatibilityJsonError("No runner compiler compatibility list provided");
880
+ }
881
+ if (!Object.prototype.hasOwnProperty.call(runnerVersions, runnerVersion)) {
882
+ throw new error_1.default.CompatibilityJsonError(`No compiler compatibility found for the runner version ${runnerVersion}`);
883
+ }
884
+ return runnerVersions[runnerVersion].safe_compiler;
614
885
  }
615
886
  }
616
887
  exports.WasmRunner = WasmRunner;