@tiens.nguyen/gonext-local-worker 1.0.310 → 1.0.311
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 +27 -2
- package/gonext_agent_chat.py +41 -6
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -1803,8 +1803,12 @@ async function runAgentChatJob(job) {
|
|
|
1803
1803
|
let cancelled = false;
|
|
1804
1804
|
// Remember this job's Ollama coding model and keep it resident via a background
|
|
1805
1805
|
// interval. Only warms now if the model is new/changed — repeat questions rely on
|
|
1806
|
-
// the interval, so we don't re-warm (or re-log) on every question.
|
|
1807
|
-
|
|
1806
|
+
// the interval, so we don't re-warm (or re-log) on every question. The warm-up uses
|
|
1807
|
+
// Ollama's native /api/generate, so SKIP it for an OpenAI-compatible coder (task #108):
|
|
1808
|
+
// that endpoint doesn't exist there and we shouldn't hit a paid API with keep-warm pings.
|
|
1809
|
+
if (payload?.codingKind !== "openai") {
|
|
1810
|
+
trackCodingModelForWarmup(payload?.codingBaseURL ?? "", payload?.codingModelId ?? "");
|
|
1811
|
+
}
|
|
1808
1812
|
const runRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
|
|
1809
1813
|
method: "PATCH",
|
|
1810
1814
|
body: JSON.stringify({ jobStatus: "running" }),
|
|
@@ -1898,6 +1902,25 @@ async function runAgentChatJob(job) {
|
|
|
1898
1902
|
jobActiveWorkspace = webWork;
|
|
1899
1903
|
}
|
|
1900
1904
|
|
|
1905
|
+
// Task #108: coding backend kind ("ollama" | "openai" | "" = Auto/sniff). For an
|
|
1906
|
+
// OpenAI-compatible coder, fetch the API key from a dedicated worker-authed endpoint
|
|
1907
|
+
// (NOT the job payload/Mongo doc — secret hygiene) and inject it into python stdin only.
|
|
1908
|
+
const codingKind = payload?.codingKind === "ollama" || payload?.codingKind === "openai"
|
|
1909
|
+
? payload.codingKind
|
|
1910
|
+
: "";
|
|
1911
|
+
let codingApiKey = "";
|
|
1912
|
+
if (codingKind === "openai") {
|
|
1913
|
+
try {
|
|
1914
|
+
const kr = await workerFetch("/api/worker/coding-key");
|
|
1915
|
+
if (kr.ok) {
|
|
1916
|
+
const kj = await kr.json().catch(() => ({}));
|
|
1917
|
+
if (typeof kj?.apiKey === "string") codingApiKey = kj.apiKey;
|
|
1918
|
+
}
|
|
1919
|
+
} catch {
|
|
1920
|
+
/* best-effort — python falls back to a keyless call, which the endpoint may 401 */
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1901
1924
|
const input = JSON.stringify({
|
|
1902
1925
|
messages: payload?.messages ?? [],
|
|
1903
1926
|
agentBaseURL: payload?.agentBaseURL ?? "",
|
|
@@ -1905,6 +1928,8 @@ async function runAgentChatJob(job) {
|
|
|
1905
1928
|
agentModelId: payload?.agentModelId ?? "",
|
|
1906
1929
|
codingBaseURL: payload?.codingBaseURL ?? "",
|
|
1907
1930
|
codingModelId: payload?.codingModelId ?? "",
|
|
1931
|
+
codingKind,
|
|
1932
|
+
codingApiKey,
|
|
1908
1933
|
// Task #107: when true, python emits a {type:code_response} event per code-model
|
|
1909
1934
|
// call so the worker persists the full raw text to Mongo.
|
|
1910
1935
|
saveFullResponse: payload?.saveFullResponse === true,
|
package/gonext_agent_chat.py
CHANGED
|
@@ -3123,6 +3123,14 @@ def run_agent_chat(cfg):
|
|
|
3123
3123
|
# also fixes an older API layer that appended /v1 after the native /api path).
|
|
3124
3124
|
raw_coding_base = _normalize_openai_base(cfg.get("codingBaseURL") or "")
|
|
3125
3125
|
raw_coding_model = (cfg.get("codingModelId") or "").strip()
|
|
3126
|
+
# Task #108: explicit coding backend kind + API key for an OpenAI-compatible endpoint.
|
|
3127
|
+
# coding_kind: "ollama" | "openai" | "" (Auto → sniff by URL). coding_api_key: the
|
|
3128
|
+
# Bearer key for an OpenAI-compatible coder (empty otherwise). The key is used for the
|
|
3129
|
+
# code-model calls (and coding-model auto-detect), NOT the chat model's agent_api_key.
|
|
3130
|
+
coding_kind = (cfg.get("codingKind") or "").strip().lower()
|
|
3131
|
+
if coding_kind not in ("ollama", "openai"):
|
|
3132
|
+
coding_kind = ""
|
|
3133
|
+
coding_api_key = (cfg.get("codingApiKey") or "").strip()
|
|
3126
3134
|
# Task #107: when on, emit each code-model response so the worker persists it to Mongo.
|
|
3127
3135
|
save_full_response = bool(cfg.get("saveFullResponse"))
|
|
3128
3136
|
if raw_coding_base:
|
|
@@ -3142,7 +3150,8 @@ def run_agent_chat(cfg):
|
|
|
3142
3150
|
# A DIFFERENT dedicated coding server. If no model name was given, ask the
|
|
3143
3151
|
# server which model it serves (mlx_lm.server otherwise tries to download a
|
|
3144
3152
|
# mismatched name from HF and 404s).
|
|
3145
|
-
detected = raw_coding_model or _detect_model_id(
|
|
3153
|
+
detected = raw_coding_model or _detect_model_id(
|
|
3154
|
+
raw_coding_base, coding_api_key or agent_api_key)
|
|
3146
3155
|
if detected:
|
|
3147
3156
|
coding_base_url = raw_coding_base
|
|
3148
3157
|
coding_model_id = detected
|
|
@@ -3156,6 +3165,15 @@ def run_agent_chat(cfg):
|
|
|
3156
3165
|
else:
|
|
3157
3166
|
coding_base_url = agent_base_url
|
|
3158
3167
|
coding_model_id = agent_model_id
|
|
3168
|
+
# Task #108: an OpenAI-compatible coder REQUIRES an explicit model name (its
|
|
3169
|
+
# /v1/chat/completions needs a `model` id, and we won't silently reuse the MLX chat
|
|
3170
|
+
# model's name against a cloud endpoint). Fail with a clear, actionable message.
|
|
3171
|
+
if coding_kind == "openai" and not (raw_coding_model or "").strip():
|
|
3172
|
+
raise _AgentConfigError(
|
|
3173
|
+
"Your agent coding model is set to OpenAI-compatible but has no model name. "
|
|
3174
|
+
"Enter the exact model id your endpoint expects (e.g. gpt-4o-mini) in "
|
|
3175
|
+
"Settings → Agent → Agent coding model name."
|
|
3176
|
+
)
|
|
3159
3177
|
# Multi-step ReAct loop (thinking agent): the agent may take several
|
|
3160
3178
|
# Thought → tool → Observation steps and then call final_answer() itself. This
|
|
3161
3179
|
# replaced the old strict single-shot (max_steps=1) once a stronger coding model
|
|
@@ -5534,17 +5552,34 @@ def run_agent_chat(cfg):
|
|
|
5534
5552
|
# the live test — minutes per step on a slow GPU — so disable it explicitly.
|
|
5535
5553
|
# Sniffed per-server because MLX might reject the unknown param.
|
|
5536
5554
|
extra_model_kwargs = {}
|
|
5537
|
-
|
|
5555
|
+
# Task #108: the explicit backend kind (from web Settings) OVERRIDES the URL-sniff.
|
|
5556
|
+
# Auto ("") → sniff by URL (today's behavior; correctly False for MLX). Only Ollama
|
|
5557
|
+
# gets reasoning_effort='none' (MLX/OpenAI reject or don't need it).
|
|
5558
|
+
if coding_kind == "ollama":
|
|
5559
|
+
coding_is_ollama = True
|
|
5560
|
+
elif coding_kind == "openai":
|
|
5561
|
+
coding_is_ollama = False
|
|
5562
|
+
else:
|
|
5563
|
+
coding_is_ollama = _is_ollama_server(coding_base_url)
|
|
5538
5564
|
if coding_is_ollama:
|
|
5539
5565
|
_log("coding server is Ollama → reasoning_effort='none' (disable thinking) + streaming")
|
|
5540
5566
|
extra_model_kwargs["reasoning_effort"] = "none"
|
|
5567
|
+
# Stream for Ollama AND for OpenAI-compatible remotes (task #108/#104): a remote
|
|
5568
|
+
# endpoint behind a proxy hits the same idle read-timeout streaming avoids, and
|
|
5569
|
+
# stream_options.include_usage keeps output-token counting working. Only a LOCAL
|
|
5570
|
+
# MLX coder (Auto, non-ollama) stays non-streaming.
|
|
5571
|
+
_stream_coder = coding_is_ollama or coding_kind == "openai"
|
|
5572
|
+
_log(f"coding backend: kind={coding_kind or 'auto'} ollama={coding_is_ollama} "
|
|
5573
|
+
f"stream={_stream_coder} key={'yes' if coding_api_key else 'no'}")
|
|
5541
5574
|
model = _LoggingModel(
|
|
5542
5575
|
model_id=coding_model_id,
|
|
5543
5576
|
api_base=coding_base_url,
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
#
|
|
5547
|
-
|
|
5577
|
+
# Task #108: use the OpenAI-compatible coding key when set, else the chat key.
|
|
5578
|
+
api_key=coding_api_key or agent_api_key,
|
|
5579
|
+
# Stream for Ollama / OpenAI-compatible so a long generation keeps the connection
|
|
5580
|
+
# warm and can't trip a reverse-proxy idle read-timeout (smolagents still gets the
|
|
5581
|
+
# full text). Only a local MLX coder stays non-streaming.
|
|
5582
|
+
stream_agent=_stream_coder,
|
|
5548
5583
|
# Task #107: persist each code-model response to Mongo when the user opted in.
|
|
5549
5584
|
save_full_response=save_full_response,
|
|
5550
5585
|
# Cap each HTTP attempt at 600s so a genuinely slow single call (a
|
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.311",
|
|
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",
|