@melaya/runner 1.0.42 → 1.0.43
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/dist/pythonEnv.js +58 -14
- package/package.json +1 -1
package/dist/pythonEnv.js
CHANGED
|
@@ -170,8 +170,62 @@ function runProc(cmd, args, onLine, envExtra = {}) {
|
|
|
170
170
|
child.on("error", () => resolve(1));
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Idempotent: ensures NLTK's tokenizer data is present in ~/nltk_data so
|
|
175
|
+
* agentscope.rag readers (default split_by="sentence") can chunk without
|
|
176
|
+
* silently failing. Safe to call on every runner launch — the downloader
|
|
177
|
+
* skips packages that are already present and finishes in <100ms.
|
|
178
|
+
*
|
|
179
|
+
* Why this is outside the venv-rebuild guard: previously this was only
|
|
180
|
+
* triggered when PIP_DEPS changed, so any runner box that already had a
|
|
181
|
+
* matching marker would skip the download. Result: Mode B ingest failed
|
|
182
|
+
* with `Resource 'punkt_tab' not found` for every file. Moving it here
|
|
183
|
+
* makes it self-healing on every invocation.
|
|
184
|
+
*/
|
|
185
|
+
async function ensureNltkData(onProgress) {
|
|
186
|
+
if (!existsSync(venvPython()))
|
|
187
|
+
return; // venv not built yet, will get caught later
|
|
188
|
+
const pyCode = [
|
|
189
|
+
"import sys, os",
|
|
190
|
+
"try:",
|
|
191
|
+
" import nltk",
|
|
192
|
+
"except ImportError:",
|
|
193
|
+
" print('nltk_missing'); sys.exit(0)",
|
|
194
|
+
"needed = ['punkt', 'punkt_tab']",
|
|
195
|
+
"missing = []",
|
|
196
|
+
"for p in needed:",
|
|
197
|
+
" try: nltk.data.find(f'tokenizers/{p}')",
|
|
198
|
+
" except LookupError: missing.append(p)",
|
|
199
|
+
"if not missing:",
|
|
200
|
+
" print('nltk_data_ok'); sys.exit(0)",
|
|
201
|
+
"print('downloading: ' + ', '.join(missing))",
|
|
202
|
+
"ok_all = True",
|
|
203
|
+
"for p in missing:",
|
|
204
|
+
" try:",
|
|
205
|
+
" if not nltk.download(p, quiet=True):",
|
|
206
|
+
" ok_all = False",
|
|
207
|
+
" print(f'nltk.download({p!r}) returned False')",
|
|
208
|
+
" except Exception as exc:",
|
|
209
|
+
" ok_all = False",
|
|
210
|
+
" print(f'nltk.download({p!r}) raised {type(exc).__name__}: {exc}')",
|
|
211
|
+
"print('nltk_data_ok' if ok_all else 'nltk_data_partial')",
|
|
212
|
+
].join("\n");
|
|
213
|
+
const code = await runProc(venvPython(), ["-c", pyCode], (line) => {
|
|
214
|
+
// Surface meaningful lines, skip generic nltk download chatter.
|
|
215
|
+
if (/^downloading|^nltk_data_|raised|returned False|nltk_missing/.test(line)) {
|
|
216
|
+
onProgress(`nltk: ${line}`);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
if (code !== 0) {
|
|
220
|
+
onProgress(`(nltk check exited ${code} — Mode B vector RAG ingest may fail with "Resource 'punkt_tab' not found")`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
173
223
|
export async function ensurePythonEnv(systemPython, expectedVersion, onProgress = (m) => console.log(chalk.gray(` [venv] ${m}`))) {
|
|
174
224
|
if (venvIsValid(expectedVersion)) {
|
|
225
|
+
// Self-heal: ensure NLTK data is present even when the venv marker
|
|
226
|
+
// says we're up to date. This is what previously broke for boxes
|
|
227
|
+
// that upgraded the runner without invalidating the marker.
|
|
228
|
+
await ensureNltkData(onProgress);
|
|
175
229
|
return { ok: true, pythonPath: venvPython() };
|
|
176
230
|
}
|
|
177
231
|
if (!existsSync(AGENTSCOPE)) {
|
|
@@ -240,20 +294,10 @@ export async function ensurePythonEnv(systemPython, expectedVersion, onProgress
|
|
|
240
294
|
if (scrCode !== 0) {
|
|
241
295
|
onProgress(`(scrapling install exited ${scrCode} — fast-path fetcher still works; stealth-rescue disabled)`);
|
|
242
296
|
}
|
|
243
|
-
// NLTK tokenizer data —
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
// CDN that silently no-ops and EVERY file fails with "Resource
|
|
248
|
-
// 'punkt_tab' not found". We download here, once, so the data is on
|
|
249
|
-
// disk before the first ingest runs. Non-fatal: if the download fails
|
|
250
|
-
// we log it; the user can manually run
|
|
251
|
-
// `python -m nltk.downloader punkt punkt_tab` once with network.
|
|
252
|
-
onProgress("downloading NLTK tokenizer data (punkt, punkt_tab) — one-time, ~3MB");
|
|
253
|
-
const nltkCode = await runProc(venvPython(), ["-m", "nltk.downloader", "punkt", "punkt_tab"], onProgress);
|
|
254
|
-
if (nltkCode !== 0) {
|
|
255
|
-
onProgress(`(nltk.downloader exited ${nltkCode} — Mode B vector RAG ingest will fail with "Resource 'punkt_tab' not found" until you run \`python -m nltk.downloader punkt punkt_tab\` manually with network access)`);
|
|
256
|
-
}
|
|
297
|
+
// NLTK tokenizer data — same idempotent check the always-runs path uses.
|
|
298
|
+
// Centralised in `ensureNltkData()` so the success/failure surface is
|
|
299
|
+
// identical whether the venv is fresh or already valid.
|
|
300
|
+
await ensureNltkData(onProgress);
|
|
257
301
|
writeFileSync(VENV_MARK, venvMarkerValue(expectedVersion), "utf-8");
|
|
258
302
|
// sanity: confirm shortuuid + agentscope (via PYTHONPATH) resolve now.
|
|
259
303
|
// Probe must mirror the spawn env so PYTHONPATH=CACHE_DIR points at
|