@tiens.nguyen/gonext-local-worker 1.0.317 → 1.0.318

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.
@@ -3177,15 +3177,32 @@ def run_agent_chat(cfg):
3177
3177
  # Multi-step ReAct loop (thinking agent): the agent may take several
3178
3178
  # Thought → tool → Observation steps and then call final_answer() itself. This
3179
3179
  # replaced the old strict single-shot (max_steps=1) once a stronger coding model
3180
- # (Qwen3-14B class) made real multi-step reasoning reliable. Default budget 8,
3181
- # overridable via cfg.maxSteps. The provide_final_answer override below is now only
3182
- # the exhaustion fallback (loop ends without final_answer).
3180
+ # (Qwen3-14B class) made real multi-step reasoning reliable. The default budget is
3181
+ # BACKEND-AWARE (a cloud OpenAI-compatible coder is stronger and cheaper-per-step than
3182
+ # a small local model, so it gets more room; a local Ollama coder gets a middle budget;
3183
+ # local MLX keeps the original tight default). Overridable via cfg.maxSteps.
3184
+ # The provide_final_answer override below is only the exhaustion fallback.
3185
+ #
3186
+ # Resolve the effective backend the SAME way as the model setup further down
3187
+ # (explicit kind wins; Auto sniffs the URL) so the budgets match the coder that runs.
3188
+ if coding_kind == "openai":
3189
+ _budget_is_ollama = False
3190
+ elif coding_kind == "ollama":
3191
+ _budget_is_ollama = True
3192
+ else:
3193
+ _budget_is_ollama = _is_ollama_server(coding_base_url)
3194
+ if coding_kind == "openai":
3195
+ _default_budget, _ws_budget = 20, 40 # OpenAI-compatible coder
3196
+ elif _budget_is_ollama:
3197
+ _default_budget, _ws_budget = 10, 20 # Ollama coder
3198
+ else:
3199
+ _default_budget, _ws_budget = 5, 12 # local MLX / Auto — original defaults
3183
3200
  try:
3184
- max_steps = int(cfg.get("maxSteps") or 5)
3201
+ max_steps = int(cfg.get("maxSteps") or _default_budget)
3185
3202
  except (TypeError, ValueError):
3186
- max_steps = 8
3203
+ max_steps = _default_budget
3187
3204
  if max_steps < 1:
3188
- max_steps = 8
3205
+ max_steps = _default_budget
3189
3206
 
3190
3207
  # Max web_search + fetch_url calls per task (user-configurable in web Settings →
3191
3208
  # Agent). Default 10; past it the retrieval tools refuse and steer to finish.
@@ -3392,11 +3409,12 @@ def run_agent_chat(cfg):
3392
3409
  _log(f"active workspace ignored (error resolving '{_aw_raw}'): {_e}")
3393
3410
  if _WS_AVAILABLE:
3394
3411
  _log(f"workspaces: {[(w['name'], w['path'], w['allowRun']) for w in _WS_ROOTS]}")
3395
- # Coding tasks are edit→test→fix loops — 5 steps is too tight. Bump the default
3396
- # budget when workspaces are registered, unless the payload set one explicitly.
3397
- if not cfg.get("maxSteps") and max_steps < 12:
3398
- max_steps = 12
3399
- _log("workspace registered → step budget raised to 12 (no explicit maxSteps)")
3412
+ # Coding tasks are edit→test→fix loops — the normal budget is too tight. Bump to
3413
+ # the backend-aware workspace budget (openai 40 / ollama 20 / MLX 12) when
3414
+ # workspaces are registered, unless the payload set one explicitly.
3415
+ if not cfg.get("maxSteps") and max_steps < _ws_budget:
3416
+ max_steps = _ws_budget
3417
+ _log(f"workspace registered → step budget raised to {_ws_budget} (no explicit maxSteps)")
3400
3418
  # Auto-test adds a verify → fix → re-test cycle on top of the work itself, so give
3401
3419
  # it more room (unless the user pinned a budget). Kept modest so a stuck test loop
3402
3420
  # still terminates rather than burning a huge budget.
@@ -5163,14 +5181,27 @@ def run_agent_chat(cfg):
5163
5181
  # Ollama honors it). Harmless if the server ignores it — usage stays 0.
5164
5182
  completion_kwargs["stream_options"] = {"include_usage": True}
5165
5183
  content, role, in_tok, out_tok, reasoning_len = self._run_stream(completion_kwargs)
5166
- # Empty-turn retry: reasoning models (gemma4 on Ollama) sometimes spend the
5167
- # WHOLE turn in the reasoning channel and emit no content → smolagents parses an
5168
- # empty tool call and WASTES the step (seen live: 2 of 5 steps burned this way).
5169
5184
  # Retry ONCE with a hard "emit the code block now" directive appended to the
5170
5185
  # already-prepared API messages (plain dicts — no smolagents format guessing).
5171
- if not content.strip():
5172
- _log(f"empty-content turn (reasoning={reasoning_len} chars, no message) "
5173
- "→ retrying once with a 'code now' directive")
5186
+ # Two triggers:
5187
+ # (1) EMPTY content reasoning models (gemma4 on Ollama) sometimes spend the
5188
+ # WHOLE turn in the reasoning channel and emit no message.
5189
+ # (2) BROKEN code attempt — content has a dangling <code>/</code> tag but NO
5190
+ # valid pair, and #111 recovery found nothing in reasoning either (seen
5191
+ # live with Kimi K3: content = "Thought: …</code>", reasoning is plain
5192
+ # planning prose with no code). Without this, smolagents shows the ugly
5193
+ # "regex pattern <code> was not found" error and burns the step before its
5194
+ # own retry recovers. A finished PROSE answer has NO code tags, so it never
5195
+ # trips this — it flows to the auto-wrap path in generate() untouched.
5196
+ _pair_re = r"<code[^>]*>[\s\S]*?</code>"
5197
+ _has_pair = re.search(_pair_re, content or "", re.I) is not None
5198
+ _broken_code = (not _has_pair) and (
5199
+ re.search(r"</?code[\s>/]", content or "", re.I) is not None
5200
+ )
5201
+ if not content.strip() or _broken_code:
5202
+ why = "empty-content" if not content.strip() else "dangling code tag, no valid block"
5203
+ _log(f"{why} turn (reasoning={reasoning_len} chars) → retrying once with a "
5204
+ "'code now' directive")
5174
5205
  _emit({"type": "step",
5175
5206
  "text": "Model produced only analysis — nudging it to write the action…"})
5176
5207
  retry_kwargs = dict(completion_kwargs)
@@ -5178,10 +5209,15 @@ def run_agent_chat(cfg):
5178
5209
  {"role": "user", "content": self._CODE_NOW_DIRECTIVE}
5179
5210
  ]
5180
5211
  r_content, r_role, r_in, r_out, _ = self._run_stream(retry_kwargs)
5181
- if r_content.strip():
5212
+ r_has_pair = re.search(_pair_re, r_content or "", re.I) is not None
5213
+ # Use the retry when it yields a real code block, or (empty original) any
5214
+ # content. If the retry is still broken and we HAD a Thought, keep the
5215
+ # original and let smolagents' own retry take it from here — no regression.
5216
+ if r_has_pair or (not content.strip() and r_content.strip()):
5182
5217
  content, role = r_content, r_role
5183
- # Count both calls' output so token accounting isn't understated.
5184
- in_tok, out_tok = (r_in or in_tok), out_tok + r_out
5218
+ in_tok = r_in or in_tok
5219
+ # Count the retry call's output either way — it was generated/billed (#112).
5220
+ out_tok += r_out
5185
5221
  if stop_sequences and not self.supports_stop_parameter:
5186
5222
  from smolagents.models import remove_content_after_stop_sequences
5187
5223
  content = remove_content_after_stop_sequences(content, stop_sequences)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.317",
3
+ "version": "1.0.318",
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",