@tiens.nguyen/gonext-local-worker 1.0.247 → 1.0.250
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-local-worker.mjs +112 -13
- package/gonext-repl.mjs +46 -2
- package/gonext_agent_chat.py +44 -0
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -867,7 +867,7 @@ async function performHttpMeasurement(params) {
|
|
|
867
867
|
* line as they arrive. Resolves when the process exits cleanly; rejects on
|
|
868
868
|
* non-zero exit or timeout. stderr is collected and appended to error messages.
|
|
869
869
|
*/
|
|
870
|
-
function runProcessWithStreamingStdout(cmd, cmdArgs, stdinStr, timeoutMs, onLine, extraEnv, abortSignal) {
|
|
870
|
+
function runProcessWithStreamingStdout(cmd, cmdArgs, stdinStr, timeoutMs, onLine, extraEnv, abortSignal, onTimeout) {
|
|
871
871
|
return new Promise((resolve, reject) => {
|
|
872
872
|
const spawnOpts = { stdio: ["pipe", "pipe", "pipe"] };
|
|
873
873
|
if (extraEnv && Object.keys(extraEnv).length > 0) {
|
|
@@ -878,10 +878,36 @@ function runProcessWithStreamingStdout(cmd, cmdArgs, stdinStr, timeoutMs, onLine
|
|
|
878
878
|
let lineBuffer = "";
|
|
879
879
|
let timedOut = false;
|
|
880
880
|
let aborted = false;
|
|
881
|
-
|
|
881
|
+
let closed = false;
|
|
882
|
+
let timerLive = true;
|
|
883
|
+
// On the time budget: with no onTimeout handler (probe/legacy) this is a hard cap —
|
|
884
|
+
// kill immediately. With a handler (task #81, interactive terminal) ask the user
|
|
885
|
+
// whether to keep waiting; the child keeps running/streaming during the ~60s prompt.
|
|
886
|
+
// "Keep waiting" disables the cap for the rest of the run; No / no-answer kills it.
|
|
887
|
+
const fireTimeout = async () => {
|
|
888
|
+
if (!timerLive || closed || aborted) return;
|
|
889
|
+
if (typeof onTimeout !== "function") {
|
|
890
|
+
timedOut = true;
|
|
891
|
+
timerLive = false;
|
|
892
|
+
child.kill("SIGKILL");
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
let extend = false;
|
|
896
|
+
try {
|
|
897
|
+
extend = await onTimeout();
|
|
898
|
+
} catch {
|
|
899
|
+
extend = false;
|
|
900
|
+
}
|
|
901
|
+
if (closed || aborted || !timerLive) return; // finished/cancelled during the prompt
|
|
902
|
+
if (extend) {
|
|
903
|
+
timerLive = false; // ignore the time budget for the rest of this run
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
882
906
|
timedOut = true;
|
|
907
|
+
timerLive = false;
|
|
883
908
|
child.kill("SIGKILL");
|
|
884
|
-
}
|
|
909
|
+
};
|
|
910
|
+
const timer = setTimeout(fireTimeout, timeoutMs);
|
|
885
911
|
// User-requested cancel (gonext Ctrl+C): kill the Python agent so it stops burning
|
|
886
912
|
// steps/tokens. On close we resolve cleanly (not reject) — the caller checks the
|
|
887
913
|
// cancel flag and leaves the job in its already-"cancelled" state.
|
|
@@ -913,10 +939,14 @@ function runProcessWithStreamingStdout(cmd, cmdArgs, stdinStr, timeoutMs, onLine
|
|
|
913
939
|
stderr += d;
|
|
914
940
|
});
|
|
915
941
|
child.on("error", (e) => {
|
|
942
|
+
closed = true;
|
|
943
|
+
timerLive = false;
|
|
916
944
|
clearTimeout(timer);
|
|
917
945
|
reject(e);
|
|
918
946
|
});
|
|
919
947
|
child.on("close", (code) => {
|
|
948
|
+
closed = true;
|
|
949
|
+
timerLive = false;
|
|
920
950
|
clearTimeout(timer);
|
|
921
951
|
if (abortSignal) abortSignal.removeEventListener?.("abort", onAbort);
|
|
922
952
|
// Drain any remaining buffered line
|
|
@@ -1925,19 +1955,35 @@ async function runAgentChatJob(job) {
|
|
|
1925
1955
|
// re-validates). Terminal = its launch cwd; web = the temp scratch folder.
|
|
1926
1956
|
activeWorkspace: jobActiveWorkspace,
|
|
1927
1957
|
});
|
|
1928
|
-
//
|
|
1929
|
-
//
|
|
1930
|
-
//
|
|
1931
|
-
//
|
|
1932
|
-
//
|
|
1933
|
-
//
|
|
1934
|
-
//
|
|
1935
|
-
//
|
|
1936
|
-
|
|
1958
|
+
// Agent run time budget (task #81). Default 60 min; user-configurable in web Settings
|
|
1959
|
+
// (agentTimeoutMinutes → payload.agentTimeoutMs). Multi-step ReAct on a 14B/31B with a
|
|
1960
|
+
// cold prompt cache — or a remote Ollama box slow-cold-loading a big model — can run
|
|
1961
|
+
// for many minutes per step, so a generous budget is normal. On expiry the TERMINAL
|
|
1962
|
+
// (interactive) asks the user whether to keep waiting (onAgentTimeout below); "yes"
|
|
1963
|
+
// ignores the budget for the rest of the run. WEB has no picker AND a hard WS deadline
|
|
1964
|
+
// (1_860_000 ms), so it's capped at 30 min and just aborts — our clean timeout error
|
|
1965
|
+
// then reaches the user instead of a generic WS timeout. On a hard stop the python
|
|
1966
|
+
// child is SIGKILLed (shows as a BrokenPipeError in the model server log — expected).
|
|
1967
|
+
// Liveness during long model calls comes from the python heartbeat (a keepalive step
|
|
1968
|
+
// chunk every 45s), not from this cap.
|
|
1969
|
+
const DEFAULT_AGENT_TIMEOUT_MS = 3_600_000; // 1 hour
|
|
1970
|
+
const WEB_AGENT_TIMEOUT_CAP_MS = 1_800_000; // stay under the web WS deadline
|
|
1971
|
+
const isInteractiveTimeout = payload?.interactiveApproval === true;
|
|
1972
|
+
const requestedTimeoutMs =
|
|
1973
|
+
Number.isFinite(payload?.agentTimeoutMs) && payload.agentTimeoutMs > 0
|
|
1974
|
+
? payload.agentTimeoutMs
|
|
1975
|
+
: DEFAULT_AGENT_TIMEOUT_MS;
|
|
1976
|
+
const timeoutMs = isInteractiveTimeout
|
|
1977
|
+
? requestedTimeoutMs
|
|
1978
|
+
: Math.min(requestedTimeoutMs, WEB_AGENT_TIMEOUT_CAP_MS);
|
|
1937
1979
|
|
|
1938
1980
|
let inThink = false;
|
|
1939
1981
|
let finalText = "";
|
|
1940
1982
|
let answerStreamed = false; // an answer_stream delta already delivered the visible text
|
|
1983
|
+
// Task #83: running total of prompt tokens sent to the agent CODE model. Updated on
|
|
1984
|
+
// each python "tokens" event, persisted to the job doc (Mongo) so the REPL can show
|
|
1985
|
+
// it live and it survives on the completed job.
|
|
1986
|
+
let latestCodeTokens = 0;
|
|
1941
1987
|
// Raw model-stream tokens (event.type === "stream") are Python code + `Thought:`
|
|
1942
1988
|
// prose + <code>/<|"|> fragments. The web Thought panel renders reasoning as
|
|
1943
1989
|
// markdown, which turns `#`-prefixed code comments into <h1> headings and
|
|
@@ -1985,6 +2031,45 @@ async function runAgentChatJob(job) {
|
|
|
1985
2031
|
}, 1500);
|
|
1986
2032
|
cancelAc.signal.addEventListener("abort", () => { cancelled = true; }, { once: true });
|
|
1987
2033
|
|
|
2034
|
+
// Task #81: when the time budget is reached in the TERMINAL, ask the user whether to
|
|
2035
|
+
// keep waiting instead of just aborting. Reuses the #67 approval plumbing
|
|
2036
|
+
// (approval-request → poll approvalDecision) with a __TIMEOUT__:: sentinel the REPL
|
|
2037
|
+
// recognizes and renders as a Yes/No picker (auto-No at 60s). Returns true = keep
|
|
2038
|
+
// waiting (disable the cap), false = stop. Only wired for interactive jobs (web never
|
|
2039
|
+
// reaches here — it isn't passed as onTimeout).
|
|
2040
|
+
const onAgentTimeout = async () => {
|
|
2041
|
+
if (!isInteractiveTimeout || !jobId) return false;
|
|
2042
|
+
const approvalId = `timeout-${Date.now()}`;
|
|
2043
|
+
const mins = Math.round(timeoutMs / 60000);
|
|
2044
|
+
const command = `__TIMEOUT__::The agent has run for ${mins} min (the time budget). Keep waiting?`;
|
|
2045
|
+
try {
|
|
2046
|
+
const r = await workerFetch(`/api/worker/jobs/${jobId}/approval-request`, {
|
|
2047
|
+
method: "POST",
|
|
2048
|
+
body: JSON.stringify({ id: approvalId, command }),
|
|
2049
|
+
});
|
|
2050
|
+
if (!r.ok) return false;
|
|
2051
|
+
} catch {
|
|
2052
|
+
return false;
|
|
2053
|
+
}
|
|
2054
|
+
// Poll for the user's Yes/No up to ~65s (the REPL auto-answers No at 60s).
|
|
2055
|
+
const deadline = Date.now() + 65_000;
|
|
2056
|
+
while (Date.now() < deadline) {
|
|
2057
|
+
if (cancelAc.signal.aborted) return false;
|
|
2058
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
2059
|
+
try {
|
|
2060
|
+
const r = await workerFetch(`/api/worker/jobs/${jobId}`);
|
|
2061
|
+
if (r.ok) {
|
|
2062
|
+
const j = await r.json().catch(() => ({}));
|
|
2063
|
+
const dec = j?.approvalDecision;
|
|
2064
|
+
if (dec && dec.id === approvalId) return dec.allow === true;
|
|
2065
|
+
}
|
|
2066
|
+
} catch {
|
|
2067
|
+
/* transient poll error — keep trying */
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
return false; // no answer in time → treat as No (stop)
|
|
2071
|
+
};
|
|
2072
|
+
|
|
1988
2073
|
try {
|
|
1989
2074
|
await runProcessWithStreamingStdout(python, [scriptPath], input, timeoutMs, (event) => {
|
|
1990
2075
|
if (event.type === "log" && typeof event.text === "string") {
|
|
@@ -2025,6 +2110,19 @@ async function runAgentChatJob(job) {
|
|
|
2025
2110
|
}
|
|
2026
2111
|
enqueueText(event.text);
|
|
2027
2112
|
answerStreamed = true;
|
|
2113
|
+
} else if (event.type === "tokens" && Number.isFinite(event.codeInput)) {
|
|
2114
|
+
// Prompt tokens sent to the agent CODE model (task #83). Persist the running
|
|
2115
|
+
// total to the job doc so it's in Mongo and the REPL can render it live. Steps
|
|
2116
|
+
// are seconds apart, so a fire-and-forget PATCH per event is cheap; it only
|
|
2117
|
+
// touches agentCodeTokens (never resultText/jobStatus), so it can't race the
|
|
2118
|
+
// chunk/completed writes.
|
|
2119
|
+
if (event.codeInput > latestCodeTokens) {
|
|
2120
|
+
latestCodeTokens = event.codeInput;
|
|
2121
|
+
workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
2122
|
+
method: "PATCH",
|
|
2123
|
+
body: JSON.stringify({ agentCodeTokens: latestCodeTokens }),
|
|
2124
|
+
}).catch(() => {});
|
|
2125
|
+
}
|
|
2028
2126
|
} else if (event.type === "final" && typeof event.text === "string") {
|
|
2029
2127
|
closeStreamFence();
|
|
2030
2128
|
if (inThink) {
|
|
@@ -2039,7 +2137,7 @@ async function runAgentChatJob(job) {
|
|
|
2039
2137
|
enqueueText(event.text);
|
|
2040
2138
|
}
|
|
2041
2139
|
}
|
|
2042
|
-
}, pdfEnv, cancelAc.signal);
|
|
2140
|
+
}, pdfEnv, cancelAc.signal, isInteractiveTimeout ? onAgentTimeout : undefined);
|
|
2043
2141
|
} finally {
|
|
2044
2142
|
clearInterval(cancelPoll);
|
|
2045
2143
|
}
|
|
@@ -2075,6 +2173,7 @@ async function runAgentChatJob(job) {
|
|
|
2075
2173
|
// the whole story.
|
|
2076
2174
|
resultText: answerStreamed ? fullText : finalText || fullText,
|
|
2077
2175
|
tokenCount: Math.max(1, Math.ceil((finalText || fullText).length / 4)),
|
|
2176
|
+
...(latestCodeTokens > 0 ? { agentCodeTokens: latestCodeTokens } : {}),
|
|
2078
2177
|
totalTimeSeconds,
|
|
2079
2178
|
}),
|
|
2080
2179
|
});
|
package/gonext-repl.mjs
CHANGED
|
@@ -122,6 +122,12 @@ const SPINNERS = [
|
|
|
122
122
|
// green, cyan, blue, indigo, pink, amber).
|
|
123
123
|
const WAIT_COLORS_256 = [120, 87, 111, 147, 218, 221];
|
|
124
124
|
const color256 = (n, s) => `\x1b[38;5;${n}m${s}\x1b[0m`;
|
|
125
|
+
// Compact token count: 950 → "950", 12300 → "12.3k", 1500000 → "1.5M" (task #83).
|
|
126
|
+
const fmtTokens = (n) => {
|
|
127
|
+
if (n < 1000) return String(n);
|
|
128
|
+
if (n < 1_000_000) return `${(n / 1000).toFixed(n < 100_000 ? 1 : 0)}k`;
|
|
129
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
130
|
+
};
|
|
125
131
|
const BULLET = "●"; // a completed action
|
|
126
132
|
|
|
127
133
|
// ---------- flags ----------
|
|
@@ -784,7 +790,21 @@ function onApprovalKey(key) {
|
|
|
784
790
|
// Returns { allow, dontAsk } — dontAsk is only ever true for the max-step "don't ask
|
|
785
791
|
// again this session" choice (the caller then stops sending the prompt).
|
|
786
792
|
const MAXSTEP_PREFIX = "__MAXSTEP__::";
|
|
793
|
+
const TIMEOUT_PREFIX = "__TIMEOUT__::";
|
|
787
794
|
async function approvalPrompt(command) {
|
|
795
|
+
// Time-budget prompt (task #81): the agent hit its wall-clock budget. Ask whether to
|
|
796
|
+
// keep waiting; auto-answer No after 60s of no input so the run can't hang forever.
|
|
797
|
+
if (command.startsWith(TIMEOUT_PREFIX)) {
|
|
798
|
+
const label = command.slice(TIMEOUT_PREFIX.length);
|
|
799
|
+
if (!process.stdin.isTTY) return { allow: false, dontAsk: false };
|
|
800
|
+
process.stdout.write("\n" + yellow("⏳ " + label) + dim(" (no answer in 60s = stop)") + "\n");
|
|
801
|
+
const idx = await pickFromList(
|
|
802
|
+
["Yes, keep waiting", "No, stop now"],
|
|
803
|
+
0,
|
|
804
|
+
{ timeoutMs: 60000, timeoutIndex: 1 }
|
|
805
|
+
);
|
|
806
|
+
return { allow: idx === 0, dontAsk: false };
|
|
807
|
+
}
|
|
788
808
|
const isMax = command.startsWith(MAXSTEP_PREFIX);
|
|
789
809
|
if (isMax) {
|
|
790
810
|
const label = command.slice(MAXSTEP_PREFIX.length);
|
|
@@ -857,7 +877,7 @@ function onListKey(key) {
|
|
|
857
877
|
finishList(-1);
|
|
858
878
|
}
|
|
859
879
|
}
|
|
860
|
-
function pickFromList(items, initialSel = 0) {
|
|
880
|
+
function pickFromList(items, initialSel = 0, opts = {}) {
|
|
861
881
|
return new Promise((resolve) => {
|
|
862
882
|
if (!process.stdin.isTTY || items.length === 0) {
|
|
863
883
|
resolve(-1);
|
|
@@ -868,6 +888,20 @@ function pickFromList(items, initialSel = 0) {
|
|
|
868
888
|
listResolve = resolve;
|
|
869
889
|
listPickerActive = true;
|
|
870
890
|
drawList(false);
|
|
891
|
+
// Optional auto-resolve after opts.timeoutMs (task #81 time-budget prompt): if the
|
|
892
|
+
// user doesn't choose in time, finish with opts.timeoutIndex (default -1). Wrap
|
|
893
|
+
// listResolve so any real selection clears the timer.
|
|
894
|
+
if (opts && opts.timeoutMs > 0) {
|
|
895
|
+
const to = setTimeout(
|
|
896
|
+
() => finishList(opts.timeoutIndex ?? -1),
|
|
897
|
+
opts.timeoutMs
|
|
898
|
+
);
|
|
899
|
+
const orig = listResolve;
|
|
900
|
+
listResolve = (v) => {
|
|
901
|
+
clearTimeout(to);
|
|
902
|
+
orig(v);
|
|
903
|
+
};
|
|
904
|
+
}
|
|
871
905
|
});
|
|
872
906
|
}
|
|
873
907
|
|
|
@@ -1037,6 +1071,9 @@ async function runAgentTurn(history) {
|
|
|
1037
1071
|
// poll figure out how much of the FINAL answer is still unshown even when resultText
|
|
1038
1072
|
// was replaced (not extended) on completion. See the completed-branch recovery below.
|
|
1039
1073
|
let liveAnswer = "";
|
|
1074
|
+
// Running total of prompt tokens sent to the agent CODE model this turn (task #83),
|
|
1075
|
+
// read from the job doc each poll. Rendered on the thinking line after the playful word.
|
|
1076
|
+
let latestCodeTokens = 0;
|
|
1040
1077
|
// First few KB of the raw stream as consumed — a cheap fingerprint to verify the
|
|
1041
1078
|
// completed resultText really EXTENDS what we streamed (an old worker replaces it
|
|
1042
1079
|
// with the shorter bare answer, which must not be sliced by shownChars).
|
|
@@ -1111,9 +1148,12 @@ async function runAgentTurn(history) {
|
|
|
1111
1148
|
// Line 2 (indented): the SPINNER + the playful word together, both in the cycling
|
|
1112
1149
|
// color — the spinner belongs with the word (dot on the label line, spinner leading
|
|
1113
1150
|
// the flavor line).
|
|
1151
|
+
// Task #83: after the playful word, show the running agent-code-model token count
|
|
1152
|
+
// (dim), e.g. " ⠙ Caffeinating… · 12.3k tok". Only once we've seen some tokens.
|
|
1153
|
+
const tokLabel = latestCodeTokens > 0 ? dim(` · ${fmtTokens(latestCodeTokens)} tok`) : "";
|
|
1114
1154
|
process.stdout.write(
|
|
1115
1155
|
`${green(BULLET)} ${green(`${fit(primary)}… (${secs}s)`)}` +
|
|
1116
|
-
"\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}`
|
|
1156
|
+
"\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`
|
|
1117
1157
|
);
|
|
1118
1158
|
statusShown = true;
|
|
1119
1159
|
};
|
|
@@ -1379,6 +1419,10 @@ async function runAgentTurn(history) {
|
|
|
1379
1419
|
continue;
|
|
1380
1420
|
}
|
|
1381
1421
|
jobRunning = job.jobStatus === "running";
|
|
1422
|
+
// Live agent-code-model token total (task #83) — monotonic, shown on the thinking line.
|
|
1423
|
+
if (Number.isFinite(job.agentCodeTokens) && job.agentCodeTokens > latestCodeTokens) {
|
|
1424
|
+
latestCodeTokens = job.agentCodeTokens;
|
|
1425
|
+
}
|
|
1382
1426
|
const text = typeof job.resultText === "string" ? job.resultText : "";
|
|
1383
1427
|
if (job.jobStatus === "completed") {
|
|
1384
1428
|
// The job can flip to "completed" between polls with authoritative resultText that
|
package/gonext_agent_chat.py
CHANGED
|
@@ -3936,6 +3936,10 @@ def run_agent_chat(cfg):
|
|
|
3936
3936
|
# literal messages array sent to /v1/chat/completions.
|
|
3937
3937
|
class _LoggingModel(OpenAIServerModel):
|
|
3938
3938
|
def __init__(self, *a, stream_agent=False, **kw):
|
|
3939
|
+
# Running total of PROMPT (input) tokens sent to the CODE model across the
|
|
3940
|
+
# turn (task #83). Only this wrapper counts — the chat/brain helper calls
|
|
3941
|
+
# (_chat_completion) don't route through here, so this is "code model only".
|
|
3942
|
+
self._code_tokens_total = 0
|
|
3939
3943
|
# stream_agent: request the completion with stream=True and reassemble the
|
|
3940
3944
|
# deltas into one message. Enabled for Ollama, where a long single-shot
|
|
3941
3945
|
# (non-stream) generation sends nothing until the very end — a reverse proxy
|
|
@@ -4129,6 +4133,29 @@ def run_agent_chat(cfg):
|
|
|
4129
4133
|
token_usage=TokenUsage(input_tokens=in_tok, output_tokens=out_tok),
|
|
4130
4134
|
)
|
|
4131
4135
|
|
|
4136
|
+
def _estimate_prompt_tokens(self, args, kwargs):
|
|
4137
|
+
"""Fallback token estimate (~4 chars/token) from the outgoing messages, used
|
|
4138
|
+
only when the model server doesn't report prompt usage (task #83)."""
|
|
4139
|
+
msgs = kwargs.get("messages")
|
|
4140
|
+
if msgs is None and args:
|
|
4141
|
+
msgs = args[0]
|
|
4142
|
+
if not msgs:
|
|
4143
|
+
return 0
|
|
4144
|
+
chars = 0
|
|
4145
|
+
try:
|
|
4146
|
+
for m in msgs:
|
|
4147
|
+
c = m.get("content") if isinstance(m, dict) else getattr(m, "content", None)
|
|
4148
|
+
if isinstance(c, list): # multimodal content parts
|
|
4149
|
+
for part in c:
|
|
4150
|
+
t = part.get("text") if isinstance(part, dict) else None
|
|
4151
|
+
if isinstance(t, str):
|
|
4152
|
+
chars += len(t)
|
|
4153
|
+
elif isinstance(c, str):
|
|
4154
|
+
chars += len(c)
|
|
4155
|
+
except Exception: # noqa: BLE001
|
|
4156
|
+
return 0
|
|
4157
|
+
return max(1, chars // 4)
|
|
4158
|
+
|
|
4132
4159
|
def generate(self, *args, **kwargs):
|
|
4133
4160
|
# Two safeguards on each step's reply BEFORE smolagents parses it for the
|
|
4134
4161
|
# code block / final_answer:
|
|
@@ -4199,6 +4226,23 @@ def run_agent_chat(cfg):
|
|
|
4199
4226
|
raise last_err
|
|
4200
4227
|
finally:
|
|
4201
4228
|
hb_stop.set()
|
|
4229
|
+
# Task #83: tally PROMPT tokens sent to the code model this step, then emit
|
|
4230
|
+
# the running total so the worker can persist it (Mongo) and the REPL can
|
|
4231
|
+
# show it live. Best-effort — never let accounting break the run.
|
|
4232
|
+
try:
|
|
4233
|
+
tu = getattr(msg, "token_usage", None)
|
|
4234
|
+
step_in = int(getattr(tu, "input_tokens", 0) or 0) if tu else 0
|
|
4235
|
+
if step_in <= 0:
|
|
4236
|
+
step_in = self._estimate_prompt_tokens(args, kwargs)
|
|
4237
|
+
if step_in > 0:
|
|
4238
|
+
self._code_tokens_total += step_in
|
|
4239
|
+
_emit({
|
|
4240
|
+
"type": "tokens",
|
|
4241
|
+
"codeInput": self._code_tokens_total,
|
|
4242
|
+
"stepInput": step_in,
|
|
4243
|
+
})
|
|
4244
|
+
except Exception: # noqa: BLE001
|
|
4245
|
+
pass
|
|
4202
4246
|
try:
|
|
4203
4247
|
content = getattr(msg, "content", None)
|
|
4204
4248
|
if content is None:
|
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.250",
|
|
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",
|