@tiens.nguyen/gonext-local-worker 1.0.125 → 1.0.126
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 +82 -42
- package/package.json +1 -1
package/gonext_agent_chat.py
CHANGED
|
@@ -827,6 +827,33 @@ _PDF_READ_INTENT = re.compile(
|
|
|
827
827
|
)
|
|
828
828
|
|
|
829
829
|
|
|
830
|
+
# A PDF request whose CONTENT must be gathered/researched first (e.g. "get me the world
|
|
831
|
+
# cup 2026 schedule … then create a pdf"). The deterministic fast-path can only render text
|
|
832
|
+
# the user already GAVE (or a prior assistant turn) — it cannot web_search, and the model
|
|
833
|
+
# would just hallucinate live facts. When these retrieval/currency signals appear AND no
|
|
834
|
+
# document body was provided, we skip the fast-path and let the multi-step agent gather the
|
|
835
|
+
# data (web_search / fetch_url) and THEN call create_pdf with it.
|
|
836
|
+
_PDF_RESEARCH_INTENT = re.compile(
|
|
837
|
+
r"\b(get me|get the|find|research|look ?up|search|fetch|gather|collect|compile|"
|
|
838
|
+
r"latest|current|today'?s|upcoming|recent|schedules?|fixtures?|standings?|"
|
|
839
|
+
r"news|weather|prices?|scores?|results?|rankings?|statistics|stats)\b",
|
|
840
|
+
re.IGNORECASE,
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def _pdf_has_explicit_body(user_text: str) -> bool:
|
|
845
|
+
"""True when the message itself supplies the document body — a quoted span, or text
|
|
846
|
+
after a 'text:/content:/following:/data:' lead-in. Distinguishes 'make a PDF of THIS
|
|
847
|
+
text …' (fast-path OK) from 'get me X then make a PDF' (needs the agent to fetch X)."""
|
|
848
|
+
t = user_text or ""
|
|
849
|
+
for pat in (r'"([\s\S]+)"', r"“([\s\S]+)”", r"'([\s\S]+)'"):
|
|
850
|
+
m = re.search(pat, t)
|
|
851
|
+
if m and len(m.group(1).strip()) >= 40:
|
|
852
|
+
return True
|
|
853
|
+
m = re.search(r"\b(texts?|following|below|content|data)\b\s*[:\-]\s*\S", t, re.IGNORECASE)
|
|
854
|
+
return bool(m)
|
|
855
|
+
|
|
856
|
+
|
|
830
857
|
def _wants_pdf(text: str) -> bool:
|
|
831
858
|
"""True when the user is asking us to (re)generate a PDF — fresh request or a
|
|
832
859
|
follow-up referencing a target PDF — but NOT when merely asking about one, and NOT
|
|
@@ -1079,8 +1106,8 @@ def run_agent_chat(cfg):
|
|
|
1079
1106
|
# the most recent substantial assistant turn as the base document and treat the
|
|
1080
1107
|
# current user message as an edit instruction the formatter should apply.
|
|
1081
1108
|
edit_instruction = ""
|
|
1109
|
+
prior_doc = ""
|
|
1082
1110
|
if len(doc_text.strip()) < 40:
|
|
1083
|
-
prior_doc = ""
|
|
1084
1111
|
for m in reversed(messages[:last_user_idx]):
|
|
1085
1112
|
if m.get("role") != "assistant":
|
|
1086
1113
|
continue
|
|
@@ -1095,52 +1122,62 @@ def run_agent_chat(cfg):
|
|
|
1095
1122
|
if prior_doc:
|
|
1096
1123
|
edit_instruction = latest_user_text.strip()
|
|
1097
1124
|
doc_text = prior_doc
|
|
1098
|
-
doc_title = _derive_pdf_title(doc_text)
|
|
1099
|
-
_log(
|
|
1100
|
-
f"PDF fast-path: title={doc_title!r} doc_chars={len(doc_text)}"
|
|
1101
|
-
+ (f" edit={edit_instruction[:60]!r}" if edit_instruction else "")
|
|
1102
|
-
)
|
|
1103
1125
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1126
|
+
# Guard: if the PDF's content must be RESEARCHED (not provided in the message, not
|
|
1127
|
+
# from a prior turn), don't render the instruction text into a PDF — fall through to
|
|
1128
|
+
# the agent loop so it can web_search/fetch_url the facts and THEN create_pdf them.
|
|
1129
|
+
if (not prior_doc
|
|
1130
|
+
and not _pdf_has_explicit_body(latest_user_text)
|
|
1131
|
+
and _PDF_RESEARCH_INTENT.search(latest_user_text)):
|
|
1132
|
+
_log("PDF fast-path SKIPPED: content needs research (no provided body) "
|
|
1133
|
+
"→ agent loop (web_search → create_pdf)")
|
|
1134
|
+
else:
|
|
1135
|
+
doc_title = _derive_pdf_title(doc_text)
|
|
1136
|
+
_log(
|
|
1137
|
+
f"PDF fast-path: title={doc_title!r} doc_chars={len(doc_text)}"
|
|
1138
|
+
+ (f" edit={edit_instruction[:60]!r}" if edit_instruction else "")
|
|
1139
|
+
)
|
|
1109
1140
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
_log(f"PDF fast-path render error: {msg[:200]}")
|
|
1116
|
-
_emit({"type": "final", "text": msg})
|
|
1117
|
-
return
|
|
1118
|
-
except Exception as e: # noqa: BLE001
|
|
1119
|
-
_log(f"PDF fast-path render crash: {type(e).__name__}: {e!r}\n{traceback.format_exc()}")
|
|
1120
|
-
_emit({"type": "final", "text": (
|
|
1121
|
-
f"Sorry — I couldn't render that into a PDF ({type(e).__name__}: {str(e)[:160]})."
|
|
1122
|
-
)})
|
|
1123
|
-
return
|
|
1141
|
+
_emit({"type": "step", "text": "Formatting document…"})
|
|
1142
|
+
markdown_text = _format_text_for_pdf(
|
|
1143
|
+
doc_text, doc_title, coding_base_url, agent_api_key, coding_model_id,
|
|
1144
|
+
instruction=edit_instruction,
|
|
1145
|
+
)
|
|
1124
1146
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1147
|
+
_emit({"type": "step", "text": "Rendering PDF…"})
|
|
1148
|
+
try:
|
|
1149
|
+
pdf_bytes = _render_pdf_bytes(markdown_text, doc_title)
|
|
1150
|
+
except RuntimeError as e:
|
|
1151
|
+
msg = str(e)
|
|
1152
|
+
_log(f"PDF fast-path render error: {msg[:200]}")
|
|
1153
|
+
_emit({"type": "final", "text": msg})
|
|
1154
|
+
return
|
|
1155
|
+
except Exception as e: # noqa: BLE001
|
|
1156
|
+
_log(f"PDF fast-path render crash: {type(e).__name__}: {e!r}\n{traceback.format_exc()}")
|
|
1157
|
+
_emit({"type": "final", "text": (
|
|
1158
|
+
f"Sorry — I couldn't render that into a PDF ({type(e).__name__}: {str(e)[:160]})."
|
|
1159
|
+
)})
|
|
1160
|
+
return
|
|
1161
|
+
|
|
1162
|
+
_emit({"type": "step", "text": "Uploading PDF…"})
|
|
1163
|
+
try:
|
|
1164
|
+
download_url = _pdf_upload_via_api(
|
|
1165
|
+
pdf_api_base, pdf_worker_key, f"{doc_title}.pdf", pdf_bytes
|
|
1166
|
+
)
|
|
1167
|
+
except Exception as e: # noqa: BLE001
|
|
1168
|
+
_log(f"PDF fast-path upload error: {type(e).__name__}: {e!r}\n{traceback.format_exc()}")
|
|
1169
|
+
_emit({"type": "final", "text": (
|
|
1170
|
+
f"PDF was created but could not be uploaded: {str(e)[:200]}"
|
|
1171
|
+
)})
|
|
1172
|
+
return
|
|
1173
|
+
|
|
1174
|
+
out = (
|
|
1175
|
+
f"✅ Your PDF \"{doc_title}\" is ready.\n"
|
|
1176
|
+
f"Download it here (link valid for a limited time):\n{download_url}"
|
|
1129
1177
|
)
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
_emit({"type": "final", "text": (
|
|
1133
|
-
f"PDF was created but could not be uploaded: {str(e)[:200]}"
|
|
1134
|
-
)})
|
|
1178
|
+
_log(f"PDF fast-path ok title={doc_title!r} bytes={len(pdf_bytes)}")
|
|
1179
|
+
_emit({"type": "final", "text": out})
|
|
1135
1180
|
return
|
|
1136
|
-
|
|
1137
|
-
out = (
|
|
1138
|
-
f"✅ Your PDF \"{doc_title}\" is ready.\n"
|
|
1139
|
-
f"Download it here (link valid for a limited time):\n{download_url}"
|
|
1140
|
-
)
|
|
1141
|
-
_log(f"PDF fast-path ok title={doc_title!r} bytes={len(pdf_bytes)}")
|
|
1142
|
-
_emit({"type": "final", "text": out})
|
|
1143
|
-
return
|
|
1144
1181
|
# ---------------------------------------------------------------------------------
|
|
1145
1182
|
|
|
1146
1183
|
# Prepend explicit tool instructions so small models pick the right tool, never
|
|
@@ -1217,6 +1254,9 @@ def run_agent_chat(cfg):
|
|
|
1217
1254
|
"- a live/current NUMBER with no URL given (price, exchange rate, weather, score, "
|
|
1218
1255
|
"stock/crypto) -> web_search(query). Do NOT guess an API URL for these.\n"
|
|
1219
1256
|
"- 'create/make/generate/export a PDF' of some text/data -> create_pdf(text, title).\n"
|
|
1257
|
+
"- a PDF of info you must LOOK UP first (e.g. 'get the world cup schedule then make a "
|
|
1258
|
+
"pdf') -> FIRST web_search/fetch_url to gather the real facts, THEN in a later step "
|
|
1259
|
+
"create_pdf(text=<the gathered facts>, title=...). Never PDF the request wording itself.\n"
|
|
1220
1260
|
+ _pdf_read_choose_line + _email_choose_line +
|
|
1221
1261
|
"- A specific known API/URL was given -> http_request().\n"
|
|
1222
1262
|
"\n"
|
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.126",
|
|
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",
|