@tiens.nguyen/gonext-local-worker 1.0.108 → 1.0.109

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.
@@ -139,6 +139,10 @@ const OCR_TIMEOUT_MS = 180_000;
139
139
  const OCR_DEBUG = String(process.env.GONEXT_OCR_DEBUG ?? "").trim() === "1";
140
140
  const OCR_SKIP_CORRECTION = String(process.env.GONEXT_OCR_SKIP_CORRECTION ?? "").trim() === "1";
141
141
  const OCR_CORRECT_TIMEOUT_MS = 120_000;
142
+ // OCR translation/correction is hardcoded to a managed Ollama service + model.
143
+ // (The user-configurable "OCR translation model" chooser was removed.)
144
+ const OCR_TRANSLATE_OLLAMA_URL = "https://ollama1.gomarsic.cc";
145
+ const OCR_TRANSLATE_OLLAMA_MODEL = "gemma4:26b";
142
146
 
143
147
  async function workerFetch(path, init = {}) {
144
148
  const url = `${apiBase}${path.startsWith("/") ? path : `/${path}`}`;
@@ -987,16 +991,6 @@ function normalizeOcrOutput(output) {
987
991
  return lines.join("\n").trim();
988
992
  }
989
993
 
990
- function resolveOcrCorrectionModelPath(override) {
991
- if (override) {
992
- const expanded = override.replace(/^~(?=\/)/, homedir());
993
- return expanded.startsWith("/") ? expanded : join(homedir(), "mlx-models", expanded);
994
- }
995
- const fromEnv = String(process.env.GONEXT_OCR_CORRECT_MODEL_PATH ?? "").trim();
996
- if (fromEnv) return fromEnv.replace(/^~(?=\/)/, homedir());
997
- return join(homedir(), "mlx-models", "translategemma-4b-it-4bit");
998
- }
999
-
1000
994
  function normalizeCorrection(raw, originalText) {
1001
995
  let text = String(raw ?? "").replace(/\r\n/g, "\n");
1002
996
  // Strip common mlx_lm.generate noise lines
@@ -1020,38 +1014,6 @@ function normalizeCorrection(raw, originalText) {
1020
1014
  return result;
1021
1015
  }
1022
1016
 
1023
- /**
1024
- * Call a correction model that's already running as an OpenAI-compatible server
1025
- * (e.g. mlx_lm.server --model translategemma-4b-it-4bit --port 8083).
1026
- * baseUrl should be like http://127.0.0.1:8083/v1
1027
- */
1028
- async function correctOcrTextViaServer(extractedText, baseUrl) {
1029
- const systemPrompt =
1030
- "You are a text correction assistant. Fix grammar and language errors in the " +
1031
- "provided text while preserving the original meaning and language. " +
1032
- "Return only the corrected text without any explanation.";
1033
- const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/chat/completions`, {
1034
- method: "POST",
1035
- headers: { "Content-Type": "application/json" },
1036
- body: JSON.stringify({
1037
- model: "default",
1038
- messages: [
1039
- { role: "system", content: systemPrompt },
1040
- { role: "user", content: `Text:\n${extractedText}` },
1041
- ],
1042
- max_tokens: 2048,
1043
- temperature: 0,
1044
- }),
1045
- signal: AbortSignal.timeout(OCR_CORRECT_TIMEOUT_MS),
1046
- });
1047
- if (!res.ok) {
1048
- throw new Error(`Correction server ${baseUrl} returned ${res.status}`);
1049
- }
1050
- const json = await res.json();
1051
- const corrected = json?.choices?.[0]?.message?.content?.trim() || "";
1052
- return normalizeCorrection(corrected, extractedText);
1053
- }
1054
-
1055
1017
  /**
1056
1018
  * Call an Ollama host's native chat endpoint (POST {base}/api/chat) for text
1057
1019
  * correction/translation, e.g. base "https://ollama1.gomarsic.cc" model "qwen3:14b".
@@ -1088,108 +1050,35 @@ async function correctOcrTextViaOllama(extractedText, base, model) {
1088
1050
  return normalizeCorrection(corrected, extractedText);
1089
1051
  }
1090
1052
 
1091
- async function correctOcrText(extractedText, modelOverride = "") {
1053
+ async function correctOcrText(extractedText) {
1092
1054
  if (OCR_SKIP_CORRECTION) {
1093
1055
  console.log("[gonext-worker] OCR correction skipped (GONEXT_OCR_SKIP_CORRECTION=1)");
1094
1056
  return extractedText;
1095
1057
  }
1096
1058
 
1097
- const override = modelOverride.trim();
1098
-
1099
- // Ollama-backed correction/translation, e.g. "ollama:qwen3:14b@@https://ollama1.gomarsic.cc".
1100
- // The model may itself contain a colon (qwen3:14b), so only strip the leading
1101
- // "ollama:" scheme and split the base off on the "@@" delimiter.
1102
- if (override.toLowerCase().startsWith("ollama:")) {
1103
- const rest = override.slice("ollama:".length);
1104
- const at = rest.indexOf("@@");
1105
- const model = (at >= 0 ? rest.slice(0, at) : rest).trim();
1106
- const base = (at >= 0 ? rest.slice(at + 2) : "").trim().replace(/\/+$/, "");
1107
- if (!model || !base) {
1108
- console.warn(
1109
- `[gonext-worker] OCR correction: malformed ollama override "${override}" (using raw OCR text)`
1110
- );
1111
- return extractedText;
1112
- }
1113
- console.log(`[gonext-worker] OCR correction via Ollama ${base} model=${model}`);
1114
- console.log(`[gonext-worker] OCR correction input: ${extractedText.slice(0, 300)}`);
1115
- try {
1116
- const corrected = await correctOcrTextViaOllama(extractedText, base, model);
1117
- console.log(`[gonext-worker] OCR correction output: ${corrected.slice(0, 300)}`);
1118
- console.log(
1119
- `[gonext-worker] OCR correction done: ${extractedText.length} → ${corrected.length} chars`
1120
- );
1121
- return corrected;
1122
- } catch (e) {
1123
- const msg = e instanceof Error ? e.message : String(e);
1124
- console.warn(
1125
- `[gonext-worker] OCR correction Ollama failed (using raw OCR text): ${msg.slice(0, 200)}`
1126
- );
1127
- return extractedText;
1128
- }
1129
- }
1130
-
1131
- // If the override looks like a URL, call it as a running mlx_lm.server endpoint.
1132
- // e.g. "http://127.0.0.1:8083/v1" or "http://127.0.0.1:8083"
1133
- if (override.startsWith("http://") || override.startsWith("https://")) {
1134
- const baseUrl = /\/v1\/?$/i.test(override) ? override : `${override}/v1`;
1135
- console.log(`[gonext-worker] OCR correction via server ${baseUrl}`);
1136
- console.log(`[gonext-worker] OCR correction input: ${extractedText.slice(0, 300)}`);
1137
- try {
1138
- const corrected = await correctOcrTextViaServer(extractedText, baseUrl);
1139
- console.log(`[gonext-worker] OCR correction output: ${corrected.slice(0, 300)}`);
1140
- console.log(`[gonext-worker] OCR correction done: ${extractedText.length} → ${corrected.length} chars`);
1141
- return corrected;
1142
- } catch (e) {
1143
- const msg = e instanceof Error ? e.message : String(e);
1144
- console.warn(`[gonext-worker] OCR correction server failed (using raw OCR text): ${msg.slice(0, 200)}`);
1145
- return extractedText;
1146
- }
1147
- }
1148
-
1149
- // Otherwise invoke mlx_lm.generate CLI (model loads per job — no server needed).
1150
- const modelPath = resolveOcrCorrectionModelPath(override);
1151
- const prompt =
1152
- "The following text was extracted by an OCR model and may contain grammar errors, " +
1153
- "wrong language, or garbled characters. " +
1154
- "Correct any errors while preserving the original meaning and language. " +
1155
- "Return only the corrected text without any explanation.\n\n" +
1156
- `Text:\n${extractedText}`;
1157
- console.log(`[gonext-worker] OCR correction via CLI model=${modelPath}`);
1059
+ // OCR translation/correction is hardcoded to a managed Ollama service + model
1060
+ // (the user-configurable chooser was removed). Any error falls back to raw text.
1061
+ console.log(
1062
+ `[gonext-worker] OCR correction via Ollama ${OCR_TRANSLATE_OLLAMA_URL} model=${OCR_TRANSLATE_OLLAMA_MODEL}`
1063
+ );
1158
1064
  console.log(`[gonext-worker] OCR correction input: ${extractedText.slice(0, 300)}`);
1159
- // Write prompt to a temp Python script that calls mlx_lm programmatically,
1160
- // avoiding CLI arg length/escaping limits and the missing --prompt-file flag.
1161
- const scriptFile = join(tmpdir(), `gonext-ocr-correct-${Date.now()}.py`);
1162
- const promptFile = join(tmpdir(), `gonext-ocr-correct-${Date.now()}.txt`);
1163
- const pyScript = `import sys, mlx_lm
1164
- model, tokenizer = mlx_lm.load(sys.argv[1])
1165
- with open(sys.argv[2], encoding="utf-8") as f:
1166
- prompt = f.read()
1167
- result = mlx_lm.generate(model, tokenizer, prompt=prompt, max_tokens=2048, verbose=False)
1168
- print(result)
1169
- `;
1170
1065
  try {
1171
- await writeFile(scriptFile, pyScript, "utf8");
1172
- await writeFile(promptFile, prompt, "utf8");
1173
- const { stdout, stderr } = await execFile(
1174
- "python3",
1175
- [scriptFile, modelPath, promptFile],
1176
- { timeout: OCR_CORRECT_TIMEOUT_MS, maxBuffer: 10 * 1024 * 1024 }
1066
+ const corrected = await correctOcrTextViaOllama(
1067
+ extractedText,
1068
+ OCR_TRANSLATE_OLLAMA_URL,
1069
+ OCR_TRANSLATE_OLLAMA_MODEL
1177
1070
  );
1178
- if (stderr?.trim()) {
1179
- console.log(`[gonext-worker] OCR correction CLI stderr: ${stderr.trim().slice(0, 300)}`);
1180
- }
1181
- const corrected = normalizeCorrection(stdout, extractedText);
1182
1071
  console.log(`[gonext-worker] OCR correction output: ${corrected.slice(0, 300)}`);
1183
- console.log(`[gonext-worker] OCR correction done: ${extractedText.length} → ${corrected.length} chars`);
1072
+ console.log(
1073
+ `[gonext-worker] OCR correction done: ${extractedText.length} → ${corrected.length} chars`
1074
+ );
1184
1075
  return corrected;
1185
1076
  } catch (e) {
1186
- const stderr = e && typeof e === "object" && "stderr" in e ? String(e.stderr ?? "").trim().slice(0, 300) : "";
1187
1077
  const msg = e instanceof Error ? e.message : String(e);
1188
- console.warn(`[gonext-worker] OCR correction CLI failed (using raw OCR text): ${msg.slice(0, 200)}${stderr ? ` | stderr: ${stderr}` : ""}`);
1078
+ console.warn(
1079
+ `[gonext-worker] OCR correction Ollama failed (using raw OCR text): ${msg.slice(0, 200)}`
1080
+ );
1189
1081
  return extractedText;
1190
- } finally {
1191
- await rm(scriptFile, { force: true }).catch(() => {});
1192
- await rm(promptFile, { force: true }).catch(() => {});
1193
1082
  }
1194
1083
  }
1195
1084
 
@@ -1335,13 +1224,10 @@ async function runOcrJob(job) {
1335
1224
  } finally {
1336
1225
  await rm(tempDir, { recursive: true, force: true }).catch(() => {});
1337
1226
  }
1338
- // Post-process: correct grammar/language with translategemma-4b-it-4bit
1339
- // (or the model set by the user in Settings > OCR correction model).
1340
- // Falls back to raw OCR text on any error so the job never fails here.
1341
- const correctionModelOverride = typeof payload.correctionModel === "string"
1342
- ? payload.correctionModel.trim()
1343
- : "";
1344
- extractedText = await correctOcrText(extractedText, correctionModelOverride);
1227
+ // Post-process: correct grammar/language via the hardcoded managed Ollama
1228
+ // service (gemma4:26b). Falls back to raw OCR text on any error so the job
1229
+ // never fails here. (`payload.correctionModel` is no longer used.)
1230
+ extractedText = await correctOcrText(extractedText);
1345
1231
  const totalTimeSeconds = (Date.now() - start) / 1000;
1346
1232
  const doneRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
1347
1233
  method: "PATCH",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.108",
3
+ "version": "1.0.109",
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",