@tiens.nguyen/gonext-local-worker 1.0.144 → 1.0.145
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 +65 -17
- package/package.json +1 -1
package/gonext_agent_chat.py
CHANGED
|
@@ -626,6 +626,44 @@ def _plain_reply(messages: list, base_url: str, api_key: str, model_id: str) ->
|
|
|
626
626
|
return f"[Error: {e}]"
|
|
627
627
|
|
|
628
628
|
|
|
629
|
+
def _synthesize_document(gathered: list, task: str, base_url: str, api_key: str,
|
|
630
|
+
model_id: str) -> str:
|
|
631
|
+
"""Turn raw gathered research observations into ONE clean, organized document body
|
|
632
|
+
for create_pdf. This is a pure REFORMAT (organize/deduplicate facts already found —
|
|
633
|
+
no new research), so the fast local chat model handles it well. Returns "" on any
|
|
634
|
+
failure so the caller can fall back to the raw concatenation."""
|
|
635
|
+
notes = "\n\n---\n\n".join(gathered)[:12000]
|
|
636
|
+
sys_prompt = (
|
|
637
|
+
"You are a document editor. You are given a user's request and raw research "
|
|
638
|
+
"notes gathered to answer it. Produce a single, clean, well-organized document "
|
|
639
|
+
"that fulfills the request using ONLY facts present in the notes.\n"
|
|
640
|
+
"- Use a clear title, headings, and bullet lists or a markdown table where it "
|
|
641
|
+
"fits (e.g. schedules → a table of Teams | Date | Time).\n"
|
|
642
|
+
"- Deduplicate and order the information sensibly (e.g. by date).\n"
|
|
643
|
+
"- Do NOT invent facts not in the notes, and do NOT mention the research process, "
|
|
644
|
+
"steps, tools, or that anything is partial. Output ONLY the document body."
|
|
645
|
+
)
|
|
646
|
+
user_prompt = f"USER REQUEST:\n{task}\n\nRESEARCH NOTES:\n{notes}"
|
|
647
|
+
try:
|
|
648
|
+
from openai import OpenAI
|
|
649
|
+
client = OpenAI(base_url=base_url, api_key=api_key or "local",
|
|
650
|
+
max_retries=0, timeout=120)
|
|
651
|
+
resp = client.chat.completions.create(
|
|
652
|
+
model=model_id,
|
|
653
|
+
messages=[{"role": "system", "content": sys_prompt},
|
|
654
|
+
{"role": "user", "content": user_prompt}],
|
|
655
|
+
temperature=0.3,
|
|
656
|
+
max_tokens=2048,
|
|
657
|
+
)
|
|
658
|
+
out = (resp.choices[0].message.content or "").strip()
|
|
659
|
+
# A reasoning model might wrap output in <think>…</think>; strip it.
|
|
660
|
+
out = _THINK_BLOCK.sub("", out).strip()
|
|
661
|
+
return out
|
|
662
|
+
except Exception as e: # noqa: BLE001
|
|
663
|
+
_log(f"document synthesis failed ({e}) → will use raw gathered notes")
|
|
664
|
+
return ""
|
|
665
|
+
|
|
666
|
+
|
|
629
667
|
def _strip_tool_tags(text: str) -> str:
|
|
630
668
|
"""Remove the internal hint tags we append to tool output (e.g. '[SUCCESS …]',
|
|
631
669
|
'[NOTE: …]', 'Note: This URL failed …') so they never leak into the user reply."""
|
|
@@ -1444,10 +1482,12 @@ def run_agent_chat(cfg):
|
|
|
1444
1482
|
return " ".join(sorted(tokens))
|
|
1445
1483
|
|
|
1446
1484
|
# Hard research budget: at most N retrieval calls (web_search + fetch_url) per run.
|
|
1447
|
-
# Past it, retrieval tools refuse and steer to create_pdf/final_answer. Keeps
|
|
1448
|
-
#
|
|
1449
|
-
#
|
|
1450
|
-
|
|
1485
|
+
# Past it, retrieval tools refuse and steer to create_pdf/final_answer. Keeps steps
|
|
1486
|
+
# free to actually FINISH — the live failure mode was burning ALL steps on
|
|
1487
|
+
# slightly-different searches and never producing the asked-for PDF. PDF tasks get a
|
|
1488
|
+
# TIGHTER budget (2) so the model is steered to create_pdf a step sooner and has more
|
|
1489
|
+
# steps left to actually emit that call itself (avoids leaning on the fallback).
|
|
1490
|
+
_research = {"used": 0, "max": 2 if agent_pdf_requested else 3}
|
|
1451
1491
|
|
|
1452
1492
|
def _research_spend(tool_name: str) -> str:
|
|
1453
1493
|
"""Return '' if under budget (and spend 1), else a refusal steering to finish."""
|
|
@@ -2081,28 +2121,36 @@ def run_agent_chat(cfg):
|
|
|
2081
2121
|
class _ToolAgent(_AgentBase):
|
|
2082
2122
|
def provide_final_answer(self, task, *args, **kwargs):
|
|
2083
2123
|
from smolagents.models import ChatMessage, MessageRole
|
|
2084
|
-
# PDF
|
|
2085
|
-
#
|
|
2086
|
-
#
|
|
2087
|
-
#
|
|
2124
|
+
# Deterministic PDF delivery: the user asked for a PDF and the model did the
|
|
2125
|
+
# research but never emitted the create_pdf call itself (weak/slow coders often
|
|
2126
|
+
# keep researching and never converge). Rendering the gathered facts here is the
|
|
2127
|
+
# INTENDED deliverable, not an error — so deliver it cleanly, with NO apologetic
|
|
2128
|
+
# "may be partial / hit my step limit" note (that made a normal, successful PDF
|
|
2129
|
+
# look like a failure to the user).
|
|
2088
2130
|
if (agent_pdf_requested and _gathered
|
|
2089
2131
|
and not (_last_obs.get("text") or "").startswith("✅")):
|
|
2090
|
-
_log(f"
|
|
2091
|
-
"gathered
|
|
2132
|
+
_log(f"delivering PDF deterministically from {len(_gathered)} "
|
|
2133
|
+
"gathered result(s) (model didn't call create_pdf itself)")
|
|
2092
2134
|
try:
|
|
2093
|
-
|
|
2135
|
+
# Reformat the raw observations into a clean document (headings/tables)
|
|
2136
|
+
# via the fast local chat model; fall back to the raw concatenation if
|
|
2137
|
+
# synthesis fails or returns nothing.
|
|
2138
|
+
compiled = _synthesize_document(
|
|
2139
|
+
_gathered, latest_user_text,
|
|
2140
|
+
agent_base_url, agent_api_key, agent_model_id,
|
|
2141
|
+
)
|
|
2142
|
+
if compiled:
|
|
2143
|
+
_log(f"synthesized document ({len(compiled)} chars) for PDF")
|
|
2144
|
+
else:
|
|
2145
|
+
compiled = "\n\n---\n\n".join(_gathered)[:12000]
|
|
2094
2146
|
out = create_pdf(
|
|
2095
2147
|
text=compiled, title=_derive_pdf_title(latest_user_text)
|
|
2096
2148
|
)
|
|
2097
2149
|
if isinstance(out, str) and out.startswith("✅"):
|
|
2098
|
-
out += (
|
|
2099
|
-
"\n\n(Note: I hit my research step limit, so this PDF contains "
|
|
2100
|
-
"what I could gather — it may be partial.)"
|
|
2101
|
-
)
|
|
2102
2150
|
return ChatMessage(role=MessageRole.ASSISTANT, content=out)
|
|
2103
|
-
_log(f"
|
|
2151
|
+
_log(f"deterministic create_pdf did not succeed: {str(out)[:120]}")
|
|
2104
2152
|
except Exception as e: # noqa: BLE001
|
|
2105
|
-
_log(f"
|
|
2153
|
+
_log(f"deterministic create_pdf error: {type(e).__name__}: {e}")
|
|
2106
2154
|
text = (_last_obs.get("text") or "").strip()
|
|
2107
2155
|
if text:
|
|
2108
2156
|
_log(f"max-steps fallback (last tool obs) → {text[:80]}")
|
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.145",
|
|
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",
|