@tiens.nguyen/gonext-local-worker 1.0.17 → 1.0.22

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.
@@ -101,6 +101,11 @@ if (!apiBase || !workerKey) {
101
101
  process.exit(1);
102
102
  }
103
103
 
104
+ const CHUNK_PATH = "/api/worker/job-chunk";
105
+ console.log(
106
+ `[gonext-worker] API base ${apiBase} — streaming chunks POST ${apiBase}${CHUNK_PATH}`
107
+ );
108
+
104
109
  function toOpenAIMessages(messages) {
105
110
  return (Array.isArray(messages) ? messages : []).map((m) => {
106
111
  if (m.role === "user" && m.attachments?.length) {
@@ -154,18 +159,39 @@ async function runChatJob(job) {
154
159
  let buf = "";
155
160
  let flushTimer = null;
156
161
  let fullText = "";
162
+ /** Chains debounced chunk POSTs so we never PATCH `completed` while a chunk POST is still in flight. */
163
+ let flushTail = Promise.resolve();
157
164
 
158
165
  const flushChunks = async () => {
159
- flushTimer = null;
160
166
  const t = buf;
161
167
  buf = "";
162
168
  if (!t) return;
163
- const res = await workerFetch(`/api/worker/job-chunk`, {
169
+ const res = await workerFetch(CHUNK_PATH, {
164
170
  method: "POST",
165
171
  body: JSON.stringify({ jobId, text: t }),
166
172
  });
167
173
  if (!res.ok && res.status !== 204) {
168
- console.error(`[gonext-worker] chunk POST failed ${res.status} for ${jobId}`);
174
+ const snippet = (await res.text().catch(() => "")).trim().slice(0, 400);
175
+ const url = `${apiBase}${CHUNK_PATH}`;
176
+ const benign409 =
177
+ res.status === 409 && snippet.includes('"jobStatus":"completed"');
178
+ if (!benign409) {
179
+ console.error(
180
+ `[gonext-worker] job-chunk POST failed status=${res.status} url=${url} jobId=${jobId}` +
181
+ (snippet ? ` response=${snippet}` : "")
182
+ );
183
+ }
184
+ if (res.status === 404) {
185
+ if (snippet.includes("Cannot POST")) {
186
+ console.error(
187
+ "[gonext-worker] DEPLOY: Lambda/API still runs old Express without POST /api/worker/job-chunk. From repo: cd api && npm ci && sam build && sam deploy — or publish the latest bundled handler so execute-api serves current routes."
188
+ );
189
+ } else {
190
+ console.error(
191
+ '[gonext-worker] hint: JSON {"error":"Job not found"} = Dynamo lookup failed (wrong worker user vs job owner). Otherwise redeploy API.'
192
+ );
193
+ }
194
+ }
169
195
  }
170
196
  };
171
197
 
@@ -174,7 +200,12 @@ async function runChatJob(job) {
174
200
  fullText += s;
175
201
  buf += s;
176
202
  if (!flushTimer) {
177
- flushTimer = setTimeout(() => void flushChunks(), 12);
203
+ flushTimer = setTimeout(() => {
204
+ flushTimer = null;
205
+ flushTail = flushTail.then(() => flushChunks()).catch((err) => {
206
+ console.error("[gonext-worker] chunk flush error:", err);
207
+ });
208
+ }, 12);
178
209
  }
179
210
  };
180
211
 
@@ -217,6 +248,7 @@ async function runChatJob(job) {
217
248
  clearTimeout(flushTimer);
218
249
  flushTimer = null;
219
250
  }
251
+ await flushTail;
220
252
  await flushChunks();
221
253
 
222
254
  const totalTimeSeconds = (Date.now() - start) / 1000;
@@ -235,6 +267,7 @@ async function runChatJob(job) {
235
267
  clearTimeout(flushTimer);
236
268
  flushTimer = null;
237
269
  }
270
+ await flushTail;
238
271
  await flushChunks().catch(() => {});
239
272
  const message = e instanceof Error ? e.message : String(e);
240
273
  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.17",
3
+ "version": "1.0.22",
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",