@tiens.nguyen/gonext-local-worker 1.0.33 → 1.0.35
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 +32 -6
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
* - `gonext-local-worker ws-ping-test` — POST worker ws-ping (needs GONEXT_* env)
|
|
7
7
|
* - `gonext-local-worker simulate-chat [text]` — claim next chat job, push fake reply like the real worker (needs GONEXT_* env)
|
|
8
8
|
* - `gonext-local-worker` — starts polling loop (claims jobs and runs models)
|
|
9
|
+
*/
|
|
9
10
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
10
11
|
import { execFile as execFileCallback } from "node:child_process";
|
|
11
12
|
import { homedir, platform } from "node:os";
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { promisify } from "node:util";
|
|
14
15
|
import dotenv from "dotenv";
|
|
16
|
+
import OpenAI from "openai";
|
|
15
17
|
|
|
16
18
|
/** Avoid `node:child_process/promises` — not available on some Node builds / older runtimes. */
|
|
17
19
|
const execFile = promisify(execFileCallback);
|
|
18
|
-
import OpenAI from "openai";
|
|
19
20
|
|
|
20
21
|
const ENV_FILE = join(homedir(), ".gonext", "worker.env");
|
|
21
22
|
dotenv.config({ path: ENV_FILE });
|
|
@@ -112,6 +113,14 @@ async function workerFetch(path, init = {}) {
|
|
|
112
113
|
return fetch(url, { ...init, headers });
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
async function ensureWorkerOk(res, context) {
|
|
117
|
+
if (res.ok) return;
|
|
118
|
+
const snippet = (await res.text().catch(() => "")).trim().slice(0, 500);
|
|
119
|
+
throw new Error(
|
|
120
|
+
`${context} failed ${res.status}${snippet ? `: ${snippet}` : ""}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
115
124
|
if (args[0] === "ws-ping-test") {
|
|
116
125
|
if (!apiBase || !workerKey) {
|
|
117
126
|
console.error(
|
|
@@ -406,7 +415,7 @@ async function runChatJob(job) {
|
|
|
406
415
|
logModelResponseToWorker(jobId, payload.modelId, fullText);
|
|
407
416
|
|
|
408
417
|
const totalTimeSeconds = (Date.now() - start) / 1000;
|
|
409
|
-
await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
418
|
+
const doneRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
410
419
|
method: "PATCH",
|
|
411
420
|
body: JSON.stringify({
|
|
412
421
|
jobStatus: "completed",
|
|
@@ -415,6 +424,7 @@ async function runChatJob(job) {
|
|
|
415
424
|
totalTimeSeconds,
|
|
416
425
|
}),
|
|
417
426
|
});
|
|
427
|
+
await ensureWorkerOk(doneRes, `complete PATCH jobId=${jobId}`);
|
|
418
428
|
console.log(`[gonext-worker] completed ${jobId} (${totalTimeSeconds.toFixed(1)}s)`);
|
|
419
429
|
} catch (e) {
|
|
420
430
|
if (flushTimer) {
|
|
@@ -424,7 +434,7 @@ async function runChatJob(job) {
|
|
|
424
434
|
await flushTail;
|
|
425
435
|
await flushChunks().catch(() => {});
|
|
426
436
|
const message = e instanceof Error ? e.message : String(e);
|
|
427
|
-
await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
437
|
+
const failRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
428
438
|
method: "PATCH",
|
|
429
439
|
body: JSON.stringify({
|
|
430
440
|
jobStatus: "failed",
|
|
@@ -432,6 +442,13 @@ async function runChatJob(job) {
|
|
|
432
442
|
totalTimeSeconds: (Date.now() - start) / 1000,
|
|
433
443
|
}),
|
|
434
444
|
});
|
|
445
|
+
if (!failRes.ok) {
|
|
446
|
+
const snippet = (await failRes.text().catch(() => "")).trim().slice(0, 500);
|
|
447
|
+
console.error(
|
|
448
|
+
`[gonext-worker] failed status PATCH also failed ${failRes.status} jobId=${jobId}` +
|
|
449
|
+
(snippet ? ` response=${snippet}` : "")
|
|
450
|
+
);
|
|
451
|
+
}
|
|
435
452
|
console.error(`[gonext-worker] failed ${jobId}:`, message);
|
|
436
453
|
}
|
|
437
454
|
}
|
|
@@ -558,10 +575,11 @@ async function runLocalHealthJob(job) {
|
|
|
558
575
|
console.log(
|
|
559
576
|
`[gonext-worker] local_health ${jobId} start (ollamaUrls=${ollamaPayloadCount}, mlx=${payload?.mlxOpenAiBaseUrl ? "yes" : "no"})`
|
|
560
577
|
);
|
|
561
|
-
await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
578
|
+
const runRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
562
579
|
method: "PATCH",
|
|
563
580
|
body: JSON.stringify({ jobStatus: "running" }),
|
|
564
581
|
});
|
|
582
|
+
await ensureWorkerOk(runRes, `mark running local_health jobId=${jobId}`);
|
|
565
583
|
try {
|
|
566
584
|
const ollamaBases = Array.isArray(payload?.ollamaBaseUrls)
|
|
567
585
|
? payload.ollamaBaseUrls.map(normalizeBaseUrl).filter(Boolean)
|
|
@@ -664,7 +682,7 @@ async function runLocalHealthJob(job) {
|
|
|
664
682
|
mlx,
|
|
665
683
|
};
|
|
666
684
|
const totalTimeSeconds = (Date.now() - start) / 1000;
|
|
667
|
-
await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
685
|
+
const doneRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
668
686
|
method: "PATCH",
|
|
669
687
|
body: JSON.stringify({
|
|
670
688
|
jobStatus: "completed",
|
|
@@ -673,13 +691,14 @@ async function runLocalHealthJob(job) {
|
|
|
673
691
|
totalTimeSeconds,
|
|
674
692
|
}),
|
|
675
693
|
});
|
|
694
|
+
await ensureWorkerOk(doneRes, `complete local_health jobId=${jobId}`);
|
|
676
695
|
const onlineCount = ollamaSources.filter((s) => s.online).length;
|
|
677
696
|
console.log(
|
|
678
697
|
`[gonext-worker] completed local_health ${jobId} (${totalTimeSeconds.toFixed(1)}s) summary: ollamaOnline=${onlineCount}/${ollamaSources.length}, mlx=${mlx ? (mlx.online ? "online" : "offline") : "n/a"}`
|
|
679
698
|
);
|
|
680
699
|
} catch (e) {
|
|
681
700
|
const message = e instanceof Error ? e.message : String(e);
|
|
682
|
-
await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
701
|
+
const failRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
683
702
|
method: "PATCH",
|
|
684
703
|
body: JSON.stringify({
|
|
685
704
|
jobStatus: "failed",
|
|
@@ -687,6 +706,13 @@ async function runLocalHealthJob(job) {
|
|
|
687
706
|
totalTimeSeconds: (Date.now() - start) / 1000,
|
|
688
707
|
}),
|
|
689
708
|
});
|
|
709
|
+
if (!failRes.ok) {
|
|
710
|
+
const snippet = (await failRes.text().catch(() => "")).trim().slice(0, 500);
|
|
711
|
+
console.error(
|
|
712
|
+
`[gonext-worker] local_health fail PATCH also failed ${failRes.status} jobId=${jobId}` +
|
|
713
|
+
(snippet ? ` response=${snippet}` : "")
|
|
714
|
+
);
|
|
715
|
+
}
|
|
690
716
|
console.error(`[gonext-worker] failed local_health ${jobId}:`, message);
|
|
691
717
|
}
|
|
692
718
|
}
|
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.35",
|
|
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",
|