@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.
@@ -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.33",
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",