@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.
- package/README.md +137 -222
- package/dist/CancellationToken.js +2 -0
- package/dist/CancellationToken.js.map +1 -1
- package/dist/browser/logger.js +6 -2
- package/dist/browser/logger.js.map +1 -1
- package/dist/browser/template/main.template.js +1 -1
- package/dist/browser/template/main.template.js.map +1 -1
- package/dist/browser/template/worker.template.js +117 -108
- package/dist/browser/template/worker.template.js.map +1 -1
- package/dist/browser/template.js +2 -2
- package/dist/browser/template.js.map +1 -1
- package/dist/browser.d.ts +5 -5
- package/dist/browser.js +242 -257
- package/dist/browser.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/defaultExternalResolver.js +6 -15
- package/dist/defaultExternalResolver.js.map +1 -1
- package/dist/error.d.ts +5 -0
- package/dist/error.js +23 -5
- package/dist/error.js.map +1 -1
- package/dist/node/logger.d.ts +2 -1
- package/dist/node/logger.js +2 -2
- package/dist/node/logger.js.map +1 -1
- package/dist/node/logger.ts +2 -2
- package/dist/node/mockLogger.d.ts +2 -1
- package/dist/node/template/main.template.ejs +108 -69
- package/dist/node/threads/mockWorkerThread.d.ts +1 -0
- package/dist/node/threads/mockWorkerThread.js +10 -3
- package/dist/node/threads/mockWorkerThread.js.map +1 -1
- package/dist/node/threads/workerPool.d.ts +4 -5
- package/dist/node/threads/workerPool.js +8 -11
- package/dist/node/threads/workerPool.js.map +1 -1
- package/dist/node/threads/workerPool.ts +15 -10
- package/dist/node/threads/workerThread.d.ts +2 -2
- package/dist/node/threads/workerThread.js +36 -35
- package/dist/node/threads/workerThread.js.map +1 -1
- package/dist/node/threads/workerThread.ts +28 -25
- package/dist/node.d.ts +32 -7
- package/dist/node.js +602 -331
- package/dist/node.js.map +1 -1
- package/dist/responseTimeMetric.d.ts +20 -0
- package/dist/responseTimeMetric.js +159 -0
- package/dist/responseTimeMetric.js.map +1 -0
- package/dist/serializer/columnarSerializer.d.ts +9 -0
- package/dist/serializer/columnarSerializer.js +59 -30
- package/dist/serializer/columnarSerializer.js.map +1 -1
- package/dist/types.d.ts +36 -2
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +53 -59
- package/dist/utils.js.map +1 -1
- 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 (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
146
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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,
|
|
337
|
-
// eslint-disable-next-line no-plusplus
|
|
338
|
-
limit++;
|
|
445
|
+
setTimeout(checkModelStatus, 1);
|
|
339
446
|
}
|
|
340
|
-
}
|
|
341
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
});
|
|
574
|
+
});
|
|
575
|
+
}
|
|
452
576
|
}
|
|
453
577
|
/* istanbul ignore next */
|
|
454
578
|
_parseError(rawResponse) {
|
|
455
|
-
|
|
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 (
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
549
|
-
|
|
746
|
+
TextMessage: `EXECUTING`,
|
|
747
|
+
JSONPayload: { input: input, modelId: id },
|
|
550
748
|
});
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
input.request_data
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
795
|
+
TextMessage: `EXECUTE COMPLETED ${id}`,
|
|
796
|
+
JSONPayload: {
|
|
797
|
+
executeDuration: execTime,
|
|
798
|
+
runnerCallId: callid,
|
|
799
|
+
ModelId: id,
|
|
800
|
+
},
|
|
593
801
|
});
|
|
594
|
-
|
|
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
|
-
|
|
602
|
-
|
|
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
|
-
|
|
811
|
+
return res.payload;
|
|
605
812
|
}
|
|
606
|
-
|
|
607
|
-
|
|
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
|
-
|
|
611
|
-
|
|
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;
|