@tiens.nguyen/gonext-local-worker 1.0.124 → 1.0.125
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 +44 -8
- package/package.json +1 -1
package/gonext_agent_chat.py
CHANGED
|
@@ -1574,22 +1574,58 @@ def run_agent_chat(cfg):
|
|
|
1574
1574
|
# literal messages array sent to /v1/chat/completions.
|
|
1575
1575
|
class _LoggingModel(OpenAIServerModel):
|
|
1576
1576
|
def generate(self, *args, **kwargs):
|
|
1577
|
-
#
|
|
1578
|
-
#
|
|
1579
|
-
#
|
|
1580
|
-
#
|
|
1577
|
+
# Two safeguards on each step's reply BEFORE smolagents parses it for the
|
|
1578
|
+
# code block / final_answer:
|
|
1579
|
+
# 1. content=None guard — Qwen3 (and reasoning models generally) can return
|
|
1580
|
+
# message.content=None on a turn (e.g. it spent the whole completion on a
|
|
1581
|
+
# <think> trace and never emitted a code block). smolagents has NO null
|
|
1582
|
+
# guard (models.py: `content = response.choices[0].message.content`) and
|
|
1583
|
+
# feeds it straight into parse_code_blobs → `re` gets None →
|
|
1584
|
+
# "expected string or bytes-like object, got 'NoneType'" → the whole
|
|
1585
|
+
# loop spirals on parse errors and trips the WebSocket timeout. Coercing
|
|
1586
|
+
# None→"" turns that fatal crash into a normal "no code block" retry.
|
|
1587
|
+
# 2. strip any leftover Qwen3 <think>…</think> trace (belt-and-suspenders;
|
|
1588
|
+
# thinking is disabled via /no_think in _prepare_completion_kwargs, but a
|
|
1589
|
+
# stray trace must never reach the parser or leak to the user).
|
|
1581
1590
|
msg = super().generate(*args, **kwargs)
|
|
1582
1591
|
try:
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
_log(
|
|
1592
|
+
content = getattr(msg, "content", None)
|
|
1593
|
+
if content is None:
|
|
1594
|
+
_log("model returned content=None (empty/thinking-only turn) → coercing to ''")
|
|
1595
|
+
msg.content = ""
|
|
1596
|
+
elif isinstance(content, str) and "</think>" in content.lower():
|
|
1597
|
+
cleaned = _strip_think(content)
|
|
1598
|
+
_log(f"stripped <think> trace ({len(content) - len(cleaned)} chars)")
|
|
1586
1599
|
msg.content = cleaned
|
|
1587
1600
|
except Exception as e: # noqa: BLE001
|
|
1588
|
-
_log(f"
|
|
1601
|
+
_log(f"content-normalize error: {e}")
|
|
1589
1602
|
return msg
|
|
1590
1603
|
|
|
1591
1604
|
def _prepare_completion_kwargs(self, *args, **kwargs):
|
|
1592
1605
|
ck = super()._prepare_completion_kwargs(*args, **kwargs)
|
|
1606
|
+
# Disable Qwen3 "thinking" for the agent loop. Live runs showed thinking-on
|
|
1607
|
+
# spends the whole completion budget on a <think> trace and returns
|
|
1608
|
+
# content=None (no code block) → parse-error spiral → WS timeout. The
|
|
1609
|
+
# `/no_think` soft switch (recognized by the Qwen3 chat template) forces a
|
|
1610
|
+
# direct Thought+code reply every step. Injected into the SYSTEM message so it
|
|
1611
|
+
# applies to EVERY step's request, not just the first user turn.
|
|
1612
|
+
try:
|
|
1613
|
+
msgs = ck.get("messages", []) or []
|
|
1614
|
+
for m in msgs:
|
|
1615
|
+
role = m.get("role") if isinstance(m, dict) else getattr(m, "role", None)
|
|
1616
|
+
if str(role).endswith("system") or role == "system":
|
|
1617
|
+
c = m.get("content") if isinstance(m, dict) else getattr(m, "content", None)
|
|
1618
|
+
if isinstance(c, str) and "/no_think" not in c:
|
|
1619
|
+
m["content"] = c + "\n\n/no_think"
|
|
1620
|
+
elif isinstance(c, list):
|
|
1621
|
+
for part in c:
|
|
1622
|
+
if isinstance(part, dict) and isinstance(part.get("text"), str) \
|
|
1623
|
+
and "/no_think" not in part["text"]:
|
|
1624
|
+
part["text"] += "\n\n/no_think"
|
|
1625
|
+
break
|
|
1626
|
+
break
|
|
1627
|
+
except Exception as e: # noqa: BLE001
|
|
1628
|
+
_log(f"/no_think inject error: {e}")
|
|
1593
1629
|
try:
|
|
1594
1630
|
msgs = ck.get("messages", []) or []
|
|
1595
1631
|
_log(f"=== MODEL REQUEST: {len(msgs)} message(s) sent to the model ===")
|
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.125",
|
|
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",
|