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

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.
@@ -135,35 +135,107 @@ async function runChatJob(job) {
135
135
  throw new Error("Invalid chat payload: messages array is missing.");
136
136
  }
137
137
  const start = Date.now();
138
- await workerFetch(`/api/worker/jobs/${jobId}`, {
138
+ const runRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
139
139
  method: "PATCH",
140
140
  body: JSON.stringify({ jobStatus: "running" }),
141
141
  });
142
+ if (!runRes.ok) {
143
+ const errBody = await runRes.text().catch(() => "");
144
+ throw new Error(
145
+ `mark running failed ${runRes.status}${errBody ? `: ${errBody}` : ""}`
146
+ );
147
+ }
142
148
 
143
149
  const client = new OpenAI({
144
150
  baseURL: payload.baseURL,
145
151
  apiKey: payload.apiKey || "ollama",
146
152
  });
147
153
 
154
+ let buf = "";
155
+ let flushTimer = null;
156
+ let fullText = "";
157
+
158
+ const flushChunks = async () => {
159
+ flushTimer = null;
160
+ const t = buf;
161
+ buf = "";
162
+ if (!t) return;
163
+ const res = await workerFetch(`/api/worker/job-chunk`, {
164
+ method: "POST",
165
+ body: JSON.stringify({ jobId, text: t }),
166
+ });
167
+ if (!res.ok && res.status !== 204) {
168
+ console.error(`[gonext-worker] chunk POST failed ${res.status} for ${jobId}`);
169
+ }
170
+ };
171
+
172
+ const enqueueText = (s) => {
173
+ if (!s) return;
174
+ fullText += s;
175
+ buf += s;
176
+ if (!flushTimer) {
177
+ flushTimer = setTimeout(() => void flushChunks(), 12);
178
+ }
179
+ };
180
+
148
181
  try {
149
- const completion = await client.chat.completions.create({
182
+ const stream = await client.chat.completions.create({
150
183
  model: payload.modelId,
151
184
  messages: toOpenAIMessages(payload.messages),
185
+ stream: true,
152
186
  temperature: 0,
153
187
  });
154
- const text = completion.choices[0]?.message?.content ?? "";
188
+
189
+ let tokenCount = 0;
190
+ let isStartThinking = false;
191
+ let isEndThinking = false;
192
+
193
+ for await (const chunk of stream) {
194
+ const delta = chunk.choices[0]?.delta;
195
+ const content = delta?.content ?? "";
196
+ const reasoningContent = delta?.reasoning_content;
197
+ tokenCount += 1;
198
+
199
+ if (reasoningContent) {
200
+ if (!isStartThinking) {
201
+ isStartThinking = true;
202
+ enqueueText("<think>");
203
+ }
204
+ enqueueText(reasoningContent);
205
+ } else {
206
+ if (isStartThinking && !isEndThinking) {
207
+ isEndThinking = true;
208
+ enqueueText("</think>");
209
+ }
210
+ if (content) {
211
+ enqueueText(content);
212
+ }
213
+ }
214
+ }
215
+
216
+ if (flushTimer) {
217
+ clearTimeout(flushTimer);
218
+ flushTimer = null;
219
+ }
220
+ await flushChunks();
221
+
155
222
  const totalTimeSeconds = (Date.now() - start) / 1000;
156
223
  await workerFetch(`/api/worker/jobs/${jobId}`, {
157
224
  method: "PATCH",
158
225
  body: JSON.stringify({
159
226
  jobStatus: "completed",
160
- resultText: text,
161
- tokenCount: 1,
227
+ resultText: fullText,
228
+ tokenCount: Math.max(1, tokenCount),
162
229
  totalTimeSeconds,
163
230
  }),
164
231
  });
165
232
  console.log(`[gonext-worker] completed ${jobId} (${totalTimeSeconds.toFixed(1)}s)`);
166
233
  } catch (e) {
234
+ if (flushTimer) {
235
+ clearTimeout(flushTimer);
236
+ flushTimer = null;
237
+ }
238
+ await flushChunks().catch(() => {});
167
239
  const message = e instanceof Error ? e.message : String(e);
168
240
  await workerFetch(`/api/worker/jobs/${jobId}`, {
169
241
  method: "PATCH",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.13",
3
+ "version": "1.0.17",
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",