@tiens.nguyen/gonext-local-worker 1.0.54 → 1.0.55

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.
@@ -1245,7 +1245,9 @@ async function runAgentChatJob(job) {
1245
1245
  let finalText = "";
1246
1246
 
1247
1247
  await runProcessWithStreamingStdout(python, [scriptPath], input, timeoutMs, (event) => {
1248
- if (event.type === "step" && typeof event.text === "string") {
1248
+ if (event.type === "log" && typeof event.text === "string") {
1249
+ console.log(`[gonext-agent] ${event.text}`);
1250
+ } else if (event.type === "step" && typeof event.text === "string") {
1249
1251
  if (!inThink) {
1250
1252
  inThink = true;
1251
1253
  enqueueText("<think>");
@@ -12,17 +12,14 @@ Reads on stdin:
12
12
  "maxSteps": int # default 10
13
13
  }
14
14
 
15
- Emits NDJSON lines on stdout per step/final:
16
- {"type": "step", "text": "<tool call summary>"}
17
- {"type": "final", "text": "<agent final answer>"}
18
-
19
- All smolagents/rich console output goes to stderr so stdout stays clean.
20
-
21
- TLS: uses certifi CA bundle when available (macOS may lack system certs for
22
- Python urllib), with fallback to the default bundle.
15
+ Emits NDJSON lines on stdout:
16
+ {"type": "log", "text": "..."} — worker logs to console, not shown in chat
17
+ {"type": "step", "text": "..."} — shown in <think> area
18
+ {"type": "final", "text": "..."} — assistant answer
23
19
  """
24
20
  import contextlib
25
21
  import json
22
+ import re
26
23
  import sys
27
24
  import urllib.request
28
25
  import urllib.error
@@ -63,6 +60,59 @@ def _http_request_impl(method, url, headers=None, body=None, timeout=20):
63
60
  return f"Error: {e}"
64
61
 
65
62
 
63
+ def _summarise_step(step_log):
64
+ """Return a short human-readable description of an agent step."""
65
+ tool_calls = getattr(step_log, "tool_calls", None) or []
66
+ observations = getattr(step_log, "observations", None)
67
+ error = getattr(step_log, "error", None)
68
+ step_num = getattr(step_log, "step_number", None)
69
+
70
+ parts = []
71
+ for tc in tool_calls:
72
+ name = getattr(tc, "name", "")
73
+ args = getattr(tc, "arguments", None)
74
+
75
+ if name == "python_interpreter" and isinstance(args, dict):
76
+ code = args.get("code", "")
77
+ # Show the http_request call if present, else first meaningful line
78
+ m = re.search(r'http_request\s*\(\s*(?:method\s*=\s*)?[\'"]?(\w+)[\'"]?\s*,\s*(?:url\s*=\s*)?[\'"]([^\'"]+)', code)
79
+ if m:
80
+ parts.append(f"HTTP {m.group(1).upper()} {m.group(2)}")
81
+ else:
82
+ first = next(
83
+ (l.strip() for l in code.splitlines()
84
+ if l.strip() and not l.strip().startswith("#")),
85
+ code[:80],
86
+ )
87
+ parts.append(first[:120])
88
+ else:
89
+ if isinstance(args, dict):
90
+ method = args.get("method", "")
91
+ url = args.get("url", "")
92
+ if method and url:
93
+ parts.append(f"HTTP {method.upper()} {url}")
94
+ else:
95
+ parts.append(f"{name}()")
96
+ else:
97
+ parts.append(name or "tool call")
98
+
99
+ if observations:
100
+ obs = str(observations).strip()
101
+ # Keep only first line of response body for brevity
102
+ first_obs_line = obs.split("\n")[0][:200]
103
+ parts.append(f"→ {first_obs_line}")
104
+
105
+ if error:
106
+ err = str(error)
107
+ if "Import of" in err and "not allowed" in err:
108
+ parts.append("→ (import blocked — using http_request tool instead)")
109
+ else:
110
+ parts.append(f"→ Error: {err[:120]}")
111
+
112
+ label = f"Step {step_num}: " if step_num is not None else ""
113
+ return label + (" | ".join(parts) if parts else "thinking…")
114
+
115
+
66
116
  def run_agent_chat(cfg):
67
117
  try:
68
118
  from smolagents import CodeAgent, OpenAIServerModel, tool
@@ -76,7 +126,9 @@ def run_agent_chat(cfg):
76
126
  agent_model_id = cfg.get("agentModelId") or ""
77
127
  max_steps = int(cfg.get("maxSteps") or 10)
78
128
 
79
- # Build the task from the last user message; use prior turns as context.
129
+ _log(f"start model={agent_model_id!r} base={agent_base_url!r} maxSteps={max_steps}")
130
+
131
+ # Build task from the last user message; prepend prior assistant turns as context.
80
132
  task_text = ""
81
133
  context_lines = []
82
134
  for m in messages:
@@ -94,9 +146,19 @@ def run_agent_chat(cfg):
94
146
  if context_lines:
95
147
  task_text = "\n".join(context_lines) + "\n\nNow answer: " + task_text
96
148
 
149
+ # Prepend explicit tool instructions so small models don't try to import
150
+ # requests/urllib themselves — the http_request function is already available.
151
+ tool_hint = (
152
+ "You have ONE built-in function: `http_request(method, url, headers='', body='')`. "
153
+ "Call it directly — do NOT write `import requests`, `import urllib`, or any import. "
154
+ "Example: result = http_request(method='GET', url='https://example.com')\n\n"
155
+ )
156
+ task_with_hint = tool_hint + "Task: " + task_text
157
+ _log(f"task={task_text[:120]!r}")
158
+
97
159
  @tool
98
160
  def http_request(method: str, url: str, headers: str = "", body: str = "") -> str:
99
- """Perform an HTTP request and return the status code and a body preview.
161
+ """Perform an HTTP request and return the status code and body preview.
100
162
 
101
163
  Args:
102
164
  method: HTTP method (GET, POST, PUT, DELETE, etc.)
@@ -110,30 +172,16 @@ def run_agent_chat(cfg):
110
172
  parsed_headers = json.loads(headers)
111
173
  except Exception: # noqa: BLE001
112
174
  pass
113
- return _http_request_impl(method, url, parsed_headers, body or None)
175
+ result = _http_request_impl(method, url, parsed_headers, body or None)
176
+ _log(f"http_request {method.upper()} {url} → {result[:80]}")
177
+ return result
114
178
 
115
179
  def step_callback(step_log):
116
180
  try:
117
- parts = []
118
- # tool_calls is a list of ToolCall(name, arguments, id)
119
- tool_calls = getattr(step_log, "tool_calls", None) or []
120
- for tc in tool_calls:
121
- name = getattr(tc, "name", str(tc))
122
- args = getattr(tc, "arguments", None)
123
- arg_str = json.dumps(args) if args is not None else ""
124
- parts.append(f"Tool: {name}({arg_str})")
125
- observations = getattr(step_log, "observations", None)
126
- if observations:
127
- parts.append(f"→ {str(observations)[:400]}")
128
- error = getattr(step_log, "error", None)
129
- if error:
130
- parts.append(f"Error: {error}")
131
- if not parts:
132
- parts.append(str(step_log)[:300])
133
- text = " ".join(parts)
181
+ text = _summarise_step(step_log)
134
182
  except Exception as e: # noqa: BLE001
135
183
  text = f"Step: {e}"
136
-
184
+ _log(f"step: {text[:200]}")
137
185
  _emit({"type": "step", "text": text})
138
186
 
139
187
  try:
@@ -150,21 +198,24 @@ def run_agent_chat(cfg):
150
198
  executor_kwargs={"timeout_seconds": 60},
151
199
  additional_authorized_imports=["json", "urllib", "urllib.request", "urllib.error"],
152
200
  )
153
- # Emit before agent.run() so the web no-progress timer resets while the
154
- # model loads its weights and generates its first reasoning step.
155
201
  _emit({"type": "step", "text": f"Sending task to {agent_model_id}…"})
156
202
  with contextlib.redirect_stdout(sys.stderr):
157
- result = agent.run(task_text)
203
+ result = agent.run(task_with_hint)
158
204
  final_text = str(result).strip()
205
+ _log(f"done: {len(final_text)} chars")
159
206
  _emit({"type": "final", "text": final_text})
160
207
  except Exception as e: # noqa: BLE001
208
+ _log(f"agent error: {e}")
161
209
  _emit({"type": "final", "text": f"[Agent error: {e}]"})
162
210
 
163
211
 
212
+ def _log(text: str):
213
+ """Emit a log event — worker prints it to console, not forwarded to chat."""
214
+ _emit({"type": "log", "text": text})
215
+
216
+
164
217
  def _emit(obj):
165
- """Write one NDJSON line to the real stdout and flush immediately.
166
- Uses _REAL_STDOUT (captured at import time) so redirect_stdout blocks
167
- inside agent.run() don't accidentally route events to stderr."""
218
+ """Write one NDJSON line to the real stdout and flush immediately."""
168
219
  _REAL_STDOUT.write(json.dumps(obj) + "\n")
169
220
  _REAL_STDOUT.flush()
170
221
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.54",
3
+ "version": "1.0.55",
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",