@tiens.nguyen/gonext-local-worker 1.0.11 → 1.0.12
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/gonext-local-worker.mjs +105 -13
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* - `gonext-local-worker` starts polling loop
|
|
7
7
|
*/
|
|
8
8
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
9
|
-
import { homedir } from "node:os";
|
|
9
|
+
import { homedir, platform } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
|
+
import { execFile } from "node:child_process/promises";
|
|
11
12
|
import dotenv from "dotenv";
|
|
12
13
|
import OpenAI from "openai";
|
|
13
14
|
|
|
@@ -28,6 +29,9 @@ Usage:
|
|
|
28
29
|
Examples:
|
|
29
30
|
gonext-local-worker set abc123 --api-base https://hwohu56e8d.execute-api.ap-southeast-1.amazonaws.com
|
|
30
31
|
gonext-local-worker
|
|
32
|
+
|
|
33
|
+
Env (optional):
|
|
34
|
+
GONEXT_MLX_LM_PYTHON Python executable for MLX LM native probe (default: python3)
|
|
31
35
|
`);
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -228,6 +232,50 @@ async function checkOpenAiModels(base, apiKey) {
|
|
|
228
232
|
}
|
|
229
233
|
}
|
|
230
234
|
|
|
235
|
+
/** True MLX LM check: import mlx_lm in Python (macOS). Not the OpenAI HTTP surface. */
|
|
236
|
+
async function checkMlxLmNativeImport() {
|
|
237
|
+
const preferred = (process.env.GONEXT_MLX_LM_PYTHON ?? "").trim() || "python3";
|
|
238
|
+
const code = [
|
|
239
|
+
"import sys",
|
|
240
|
+
"try:",
|
|
241
|
+
" import mlx_lm",
|
|
242
|
+
" v = getattr(mlx_lm, '__version__', None)",
|
|
243
|
+
" print(v or 'ok')",
|
|
244
|
+
"except Exception:",
|
|
245
|
+
" sys.exit(1)",
|
|
246
|
+
].join("\n");
|
|
247
|
+
|
|
248
|
+
const candidates = [preferred];
|
|
249
|
+
if (preferred === "python3") candidates.push("python");
|
|
250
|
+
|
|
251
|
+
const tried = [];
|
|
252
|
+
for (const exe of [...new Set(candidates)]) {
|
|
253
|
+
tried.push(exe);
|
|
254
|
+
try {
|
|
255
|
+
const { stdout } = await execFile(exe, ["-c", code], {
|
|
256
|
+
timeout: 15000,
|
|
257
|
+
maxBuffer: 65536,
|
|
258
|
+
windowsHide: true,
|
|
259
|
+
});
|
|
260
|
+
const version = String(stdout ?? "").trim();
|
|
261
|
+
return {
|
|
262
|
+
available: true,
|
|
263
|
+
python: exe,
|
|
264
|
+
version: version || undefined,
|
|
265
|
+
method: "python_import_mlx_lm",
|
|
266
|
+
};
|
|
267
|
+
} catch {
|
|
268
|
+
/* try next */
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
available: false,
|
|
273
|
+
python: preferred,
|
|
274
|
+
method: "python_import_mlx_lm",
|
|
275
|
+
error: `Could not import mlx_lm (tried: ${tried.join(", ")})`,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
231
279
|
async function runLocalHealthJob(job) {
|
|
232
280
|
const { jobId, payload } = job;
|
|
233
281
|
const start = Date.now();
|
|
@@ -269,15 +317,66 @@ async function runLocalHealthJob(job) {
|
|
|
269
317
|
}
|
|
270
318
|
}
|
|
271
319
|
const mlxRoot = normalizeOpenAiV1Root(payload?.mlxOpenAiBaseUrl);
|
|
272
|
-
let
|
|
320
|
+
let mlxHttp = null;
|
|
321
|
+
let mlxNative = null;
|
|
322
|
+
|
|
273
323
|
if (mlxRoot) {
|
|
274
324
|
const mlxStart = Date.now();
|
|
275
|
-
console.log(`[gonext-worker] local_health ${jobId} check mlx ${mlxRoot}`);
|
|
276
|
-
|
|
325
|
+
console.log(`[gonext-worker] local_health ${jobId} check mlx HTTP ${mlxRoot}`);
|
|
326
|
+
mlxHttp = await checkOpenAiModels(mlxRoot, payload?.mlxApiKey ?? "");
|
|
277
327
|
console.log(
|
|
278
|
-
`[gonext-worker] local_health ${jobId} mlx
|
|
328
|
+
`[gonext-worker] local_health ${jobId} mlx HTTP online=${mlxHttp.online} models=${mlxHttp.models.length} took=${((Date.now() - mlxStart) / 1000).toFixed(2)}s`
|
|
279
329
|
);
|
|
280
330
|
}
|
|
331
|
+
|
|
332
|
+
const wantNativeFallback =
|
|
333
|
+
mlxRoot &&
|
|
334
|
+
payload?.mlxNativeFallback !== false &&
|
|
335
|
+
platform() === "darwin" &&
|
|
336
|
+
(!mlxHttp?.online || (mlxHttp?.models?.length ?? 0) === 0);
|
|
337
|
+
|
|
338
|
+
if (wantNativeFallback) {
|
|
339
|
+
const t0 = Date.now();
|
|
340
|
+
console.log(
|
|
341
|
+
`[gonext-worker] local_health ${jobId} mlx native probe (Python mlx_lm import)`
|
|
342
|
+
);
|
|
343
|
+
mlxNative = await checkMlxLmNativeImport();
|
|
344
|
+
console.log(
|
|
345
|
+
`[gonext-worker] local_health ${jobId} mlx native available=${mlxNative.available} took=${((Date.now() - t0) / 1000).toFixed(2)}s`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let mlx = null;
|
|
350
|
+
if (mlxRoot || mlxNative?.available) {
|
|
351
|
+
const httpOk = Boolean(mlxHttp?.online && (mlxHttp?.models?.length ?? 0) > 0);
|
|
352
|
+
const nativeOk = mlxNative?.available === true;
|
|
353
|
+
mlx = {
|
|
354
|
+
configured: httpOk || nativeOk,
|
|
355
|
+
online: httpOk || nativeOk,
|
|
356
|
+
models: httpOk
|
|
357
|
+
? mlxHttp.models
|
|
358
|
+
: nativeOk
|
|
359
|
+
? [
|
|
360
|
+
{
|
|
361
|
+
id: "mlx_lm_native",
|
|
362
|
+
name: mlxNative.version
|
|
363
|
+
? `MLX LM (${mlxNative.version})`
|
|
364
|
+
: "MLX LM (Python import OK)",
|
|
365
|
+
value: "mlx:mlx_lm_native",
|
|
366
|
+
},
|
|
367
|
+
]
|
|
368
|
+
: [],
|
|
369
|
+
endpoint: mlxHttp?.endpoint,
|
|
370
|
+
http: mlxHttp
|
|
371
|
+
? {
|
|
372
|
+
online: mlxHttp.online,
|
|
373
|
+
endpoint: mlxHttp.endpoint,
|
|
374
|
+
modelCount: mlxHttp.models.length,
|
|
375
|
+
}
|
|
376
|
+
: undefined,
|
|
377
|
+
native: mlxNative ?? undefined,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
281
380
|
const result = {
|
|
282
381
|
ollama:
|
|
283
382
|
ollamaBases.length > 0
|
|
@@ -289,14 +388,7 @@ async function runLocalHealthJob(job) {
|
|
|
289
388
|
sources: ollamaSources,
|
|
290
389
|
}
|
|
291
390
|
: undefined,
|
|
292
|
-
mlx
|
|
293
|
-
? {
|
|
294
|
-
configured: mlx.models.length > 0,
|
|
295
|
-
online: mlx.online,
|
|
296
|
-
models: mlx.models,
|
|
297
|
-
endpoint: mlx.endpoint,
|
|
298
|
-
}
|
|
299
|
-
: undefined,
|
|
391
|
+
mlx,
|
|
300
392
|
};
|
|
301
393
|
const totalTimeSeconds = (Date.now() - start) / 1000;
|
|
302
394
|
await workerFetch(`/api/worker/jobs/${jobId}`, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|