@coherentglobal/wasm-runner 0.1.19 → 0.3.2

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