@tiens.nguyen/gonext-local-worker 1.0.140 → 1.0.141

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.
@@ -634,6 +634,38 @@ def _strip_tool_tags(text: str) -> str:
634
634
 
635
635
  _THINK_BLOCK = re.compile(r"<think>.*?</think>", re.DOTALL | re.IGNORECASE)
636
636
 
637
+ # Whitespace/attr variants of smolagents' code-block tags. Weak models routinely
638
+ # emit "<code >" (trailing space), "< code >", "</ code>", or an uppercase form —
639
+ # none of which match smolagents' exact "<code>(.*?)</code>" parser, so EVERY step
640
+ # fails with "regex pattern <code>(.*?)</code> was not found" and the run burns all
641
+ # its steps. Normalizing the tags to the canonical form before the parser sees them
642
+ # turns that spiral into a normal, parseable step.
643
+ _CODE_OPEN_TAG = re.compile(r"<\s*code(?:\s[^>]*)?\s*>", re.IGNORECASE)
644
+ _CODE_CLOSE_TAG = re.compile(r"<\s*/\s*code\s*>", re.IGNORECASE)
645
+ # Fallback: a markdown code fence (```py / ```python / ```) when the model ignored
646
+ # the tag convention entirely. Captures the fenced body so we can re-wrap it.
647
+ _MD_FENCE_BLOCK = re.compile(
648
+ r"```(?:py|python)?[ \t]*\r?\n(.*?)```", re.DOTALL | re.IGNORECASE
649
+ )
650
+
651
+
652
+ def _normalize_code_tags(text: str) -> str:
653
+ """Coerce a model reply's code-block delimiters to smolagents' exact <code>…</code>.
654
+
655
+ Handles the two failure modes seen live with weak coding models:
656
+ 1. tag whitespace/case: "<code >", "< code >", "</ code>", "<CODE>" → "<code>".
657
+ 2. markdown fences instead of tags: ```py … ``` → <code> … </code> (only when
658
+ no <code> tag is present after step 1, so we don't touch code that legitimately
659
+ contains backticks inside a real <code> block).
660
+ Returns text unchanged when it already parses cleanly."""
661
+ if not text:
662
+ return text
663
+ fixed = _CODE_OPEN_TAG.sub("<code>", text)
664
+ fixed = _CODE_CLOSE_TAG.sub("</code>", fixed)
665
+ if "<code>" not in fixed:
666
+ fixed = _MD_FENCE_BLOCK.sub(lambda m: f"<code>\n{m.group(1)}</code>", fixed, count=1)
667
+ return fixed
668
+
637
669
 
638
670
  def _strip_think(text: str) -> str:
639
671
  """Remove Qwen3-style <think>…</think> reasoning traces from a model reply.
@@ -1841,10 +1873,20 @@ def run_agent_chat(cfg):
1841
1873
  if content is None:
1842
1874
  _log("model returned content=None (empty/thinking-only turn) → coercing to ''")
1843
1875
  msg.content = ""
1876
+ content = ""
1844
1877
  elif isinstance(content, str) and "</think>" in content.lower():
1845
1878
  cleaned = _strip_think(content)
1846
1879
  _log(f"stripped <think> trace ({len(content) - len(cleaned)} chars)")
1847
1880
  msg.content = cleaned
1881
+ content = cleaned
1882
+ # Normalize code-block delimiters so smolagents' <code>…</code> parser
1883
+ # matches even when the model wrote "<code >" / a markdown fence. Without
1884
+ # this, tag-whitespace variants fail EVERY step → "Reached max steps".
1885
+ if isinstance(content, str) and content:
1886
+ normalized = _normalize_code_tags(content)
1887
+ if normalized != content:
1888
+ _log("normalized code-block tags (<code >/fence → <code>)")
1889
+ msg.content = normalized
1848
1890
  except Exception as e: # noqa: BLE001
1849
1891
  _log(f"content-normalize error: {e}")
1850
1892
  return msg
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.140",
3
+ "version": "1.0.141",
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",