@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.
- package/gonext-local-worker.mjs +77 -5
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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",
|