@tiens.nguyen/gonext-local-worker 1.0.162 → 1.0.165
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_agent_chat.py +78 -12
- package/package.json +1 -1
package/gonext_agent_chat.py
CHANGED
|
@@ -1156,17 +1156,39 @@ def _rag_workdir(source_key: str) -> str:
|
|
|
1156
1156
|
return base
|
|
1157
1157
|
|
|
1158
1158
|
|
|
1159
|
-
def _rag_download(url: str, dest_path: str = "") ->
|
|
1160
|
-
"""Download a public URL
|
|
1159
|
+
def _rag_download(url: str, dest_path: str = "") -> tuple:
|
|
1160
|
+
"""Download a public URL with SSRF + size guards — IDEMPOTENT per URL. Every URL
|
|
1161
|
+
maps to a deterministic work dir (~/.gonext/rag-work/<urlHash>/); relative
|
|
1162
|
+
dest_paths are contained there (never the process cwd, which is the user's home),
|
|
1163
|
+
and a repeat call for an already-downloaded URL reuses the cached file instantly
|
|
1164
|
+
(follow-up questions re-run the pipeline cheaply). Returns (path, cached)."""
|
|
1161
1165
|
import os
|
|
1162
1166
|
src = _rag_gdrive_direct((url or "").strip())
|
|
1163
1167
|
_rag_assert_safe_url(src)
|
|
1164
1168
|
key = _rag_source_key(url)
|
|
1169
|
+
workdir = _rag_workdir(key)
|
|
1170
|
+
dest_path = os.path.expanduser((dest_path or "").strip())
|
|
1165
1171
|
if not dest_path:
|
|
1166
1172
|
name = os.path.basename(src.split("?")[0]) or "download.bin"
|
|
1167
1173
|
if not name.lower().endswith(".zip") and "zip" in src.lower():
|
|
1168
1174
|
name += ".zip"
|
|
1169
|
-
dest_path = os.path.join(
|
|
1175
|
+
dest_path = os.path.join(workdir, name)
|
|
1176
|
+
elif not os.path.isabs(dest_path):
|
|
1177
|
+
# Contain relative paths in the per-URL work dir — models pass bare names like
|
|
1178
|
+
# "project.zip" which would otherwise land in the worker's cwd (the home dir).
|
|
1179
|
+
dest_path = os.path.join(workdir, dest_path)
|
|
1180
|
+
# Cache hit 1: the exact target already exists.
|
|
1181
|
+
if os.path.isfile(dest_path) and os.path.getsize(dest_path) > 0:
|
|
1182
|
+
return dest_path, True
|
|
1183
|
+
# Cache hit 2: this URL was downloaded before under a different name — the work dir
|
|
1184
|
+
# is per-URL, so any archive already in it IS this URL's file.
|
|
1185
|
+
try:
|
|
1186
|
+
for fn in sorted(os.listdir(workdir)):
|
|
1187
|
+
p = os.path.join(workdir, fn)
|
|
1188
|
+
if os.path.isfile(p) and fn.lower().endswith(".zip") and os.path.getsize(p) > 0:
|
|
1189
|
+
return p, True
|
|
1190
|
+
except OSError:
|
|
1191
|
+
pass
|
|
1170
1192
|
os.makedirs(os.path.dirname(dest_path) or ".", exist_ok=True)
|
|
1171
1193
|
req = urllib.request.Request(src, headers={"User-Agent": "gonext-rag/1.0"})
|
|
1172
1194
|
total = 0
|
|
@@ -1184,15 +1206,48 @@ def _rag_download(url: str, dest_path: str = "") -> str:
|
|
|
1184
1206
|
f"Download exceeds the {_RAG_MAX_DOWNLOAD_BYTES // (1024*1024)} MB cap."
|
|
1185
1207
|
)
|
|
1186
1208
|
out.write(block)
|
|
1187
|
-
return dest_path
|
|
1209
|
+
return dest_path, False
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
def _rag_resolve_zip(zip_path: str) -> str:
|
|
1213
|
+
"""Resolve a (possibly relative) zip path. Bare names the model echoes from earlier
|
|
1214
|
+
turns are searched in the rag work dirs so a fresh process still finds them."""
|
|
1215
|
+
import glob as _glob
|
|
1216
|
+
import os
|
|
1217
|
+
zp = os.path.expanduser((zip_path or "").strip())
|
|
1218
|
+
if os.path.isfile(zp):
|
|
1219
|
+
return zp
|
|
1220
|
+
if not os.path.isabs(zp):
|
|
1221
|
+
hits = _glob.glob(os.path.join(
|
|
1222
|
+
os.path.expanduser("~"), ".gonext", "rag-work", "*", os.path.basename(zp)))
|
|
1223
|
+
if hits:
|
|
1224
|
+
return hits[0]
|
|
1225
|
+
return zp
|
|
1188
1226
|
|
|
1189
1227
|
|
|
1190
1228
|
def _rag_unzip(zip_path: str, dest_dir: str = "") -> tuple:
|
|
1191
|
-
"""Extract a local zip with Zip-Slip + zip-bomb guards.
|
|
1229
|
+
"""Extract a local zip with Zip-Slip + zip-bomb guards — IDEMPOTENT. If the target
|
|
1230
|
+
dir already has extracted files, reuse it instead of re-extracting. Relative
|
|
1231
|
+
dest_dirs are placed next to the zip (inside the per-URL work dir), never the cwd.
|
|
1232
|
+
Returns (dir, file_list, cached)."""
|
|
1192
1233
|
import os
|
|
1193
1234
|
import zipfile
|
|
1235
|
+
zip_path = _rag_resolve_zip(zip_path)
|
|
1236
|
+
dest_dir = os.path.expanduser((dest_dir or "").strip())
|
|
1194
1237
|
if not dest_dir:
|
|
1195
1238
|
dest_dir = os.path.splitext(zip_path)[0] + "_unzipped"
|
|
1239
|
+
elif not os.path.isabs(dest_dir):
|
|
1240
|
+
dest_dir = os.path.join(os.path.dirname(zip_path) or ".", dest_dir)
|
|
1241
|
+
# Cache hit: already extracted for this zip → return the existing listing.
|
|
1242
|
+
if os.path.isdir(dest_dir):
|
|
1243
|
+
existing = []
|
|
1244
|
+
for dirpath, _dirnames, filenames in os.walk(dest_dir):
|
|
1245
|
+
for fn in filenames:
|
|
1246
|
+
existing.append(os.path.relpath(os.path.join(dirpath, fn), dest_dir))
|
|
1247
|
+
if len(existing) > _RAG_MAX_FILES:
|
|
1248
|
+
break
|
|
1249
|
+
if existing:
|
|
1250
|
+
return dest_dir, sorted(existing), True
|
|
1196
1251
|
os.makedirs(dest_dir, exist_ok=True)
|
|
1197
1252
|
dest_root = os.path.realpath(dest_dir)
|
|
1198
1253
|
written = 0
|
|
@@ -1216,7 +1271,7 @@ def _rag_unzip(zip_path: str, dest_dir: str = "") -> tuple:
|
|
|
1216
1271
|
dst.write(src.read())
|
|
1217
1272
|
written += 1
|
|
1218
1273
|
names.append(info.filename)
|
|
1219
|
-
return dest_dir, names
|
|
1274
|
+
return dest_dir, names, False
|
|
1220
1275
|
|
|
1221
1276
|
|
|
1222
1277
|
def _rag_iter_text_files(root: str):
|
|
@@ -1776,8 +1831,11 @@ def run_agent_chat(cfg):
|
|
|
1776
1831
|
"THEN list_dir(dir) to find the README, THEN read_text_file(that README) THEN answer from it. "
|
|
1777
1832
|
"This is the FASTEST path for a summary — do NOT index unless deep Q&A across many files is needed.\n"
|
|
1778
1833
|
"- user wants deep Q&A across the WHOLE project -> download_file THEN unzip_file THEN "
|
|
1779
|
-
"rag_index(dir, source_url=url) THEN rag_search(source_url=url, query=<question>) THEN answer
|
|
1780
|
-
"
|
|
1834
|
+
"rag_index(dir, source_url=url) THEN rag_search(source_url=url, query=<question>) THEN answer.\n"
|
|
1835
|
+
"- FOLLOW-UP question about a ZIP url from EARLIER in the conversation -> try "
|
|
1836
|
+
"rag_search(source_url=url, query=<question>) FIRST. If it says no knowledge base exists, "
|
|
1837
|
+
"run download_file(url) + unzip_file(zip) again — both are CACHED per url and return "
|
|
1838
|
+
"instantly — then list_dir/read_text_file or rag_index as needed. "
|
|
1781
1839
|
"If the user adds information, rag_add(source_url=url, text=...).\n"
|
|
1782
1840
|
) if _RAG_AVAILABLE else ""
|
|
1783
1841
|
_pdf_read_tool_line = (
|
|
@@ -2718,10 +2776,14 @@ def run_agent_chat(cfg):
|
|
|
2718
2776
|
url: public http(s) URL of the file to download.
|
|
2719
2777
|
dest_path: optional local save path; leave empty for an automatic work-dir path.
|
|
2720
2778
|
"""
|
|
2721
|
-
_emit({"type": "step", "text": f"Downloading → {url[:80]}"})
|
|
2722
2779
|
try:
|
|
2723
2780
|
import os
|
|
2724
|
-
path = _rag_download(url, (dest_path or "").strip())
|
|
2781
|
+
path, cached = _rag_download(url, (dest_path or "").strip())
|
|
2782
|
+
if cached:
|
|
2783
|
+
_emit({"type": "step", "text": "Already downloaded — reusing cached file"})
|
|
2784
|
+
return (f"Already downloaded earlier — reusing {path} "
|
|
2785
|
+
f"({os.path.getsize(path)} bytes). Next: unzip_file(zip_path='{path}').")
|
|
2786
|
+
_emit({"type": "step", "text": f"Downloading → {url[:80]}"})
|
|
2725
2787
|
return f"Downloaded to {path} ({os.path.getsize(path)} bytes). Next: unzip_file(zip_path='{path}') if it is a zip."
|
|
2726
2788
|
except Exception as e: # noqa: BLE001
|
|
2727
2789
|
return f"Error: download failed: {type(e).__name__}: {e}"
|
|
@@ -2734,11 +2796,15 @@ def run_agent_chat(cfg):
|
|
|
2734
2796
|
zip_path: local path to the .zip (from download_file).
|
|
2735
2797
|
dest_dir: optional target directory; leave empty for an automatic one.
|
|
2736
2798
|
"""
|
|
2737
|
-
_emit({"type": "step", "text": "Unzipping…"})
|
|
2738
2799
|
try:
|
|
2739
|
-
d, names = _rag_unzip((zip_path or "").strip(), (dest_dir or "").strip())
|
|
2800
|
+
d, names, cached = _rag_unzip((zip_path or "").strip(), (dest_dir or "").strip())
|
|
2740
2801
|
preview = ", ".join(names[:15])
|
|
2741
2802
|
more = f" …(+{len(names) - 15} more)" if len(names) > 15 else ""
|
|
2803
|
+
if cached:
|
|
2804
|
+
_emit({"type": "step", "text": "Already unzipped — reusing extracted files"})
|
|
2805
|
+
return (f"Already unzipped earlier — {len(names)} files at {d}. "
|
|
2806
|
+
f"Files: {preview}{more}. Use list_dir/read_text_file or rag_index on it.")
|
|
2807
|
+
_emit({"type": "step", "text": "Unzipping…"})
|
|
2742
2808
|
return f"Unzipped {len(names)} files to {d}. Files: {preview}{more}. Next: rag_index(path='{d}', source_url=<the original url>)."
|
|
2743
2809
|
except Exception as e: # noqa: BLE001
|
|
2744
2810
|
return f"Error: unzip failed: {type(e).__name__}: {e}"
|
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.165",
|
|
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",
|