@tiens.nguyen/gonext-local-worker 1.0.332 → 1.0.334

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.
@@ -1832,8 +1832,7 @@ function _isLocalCodingUrl(baseURL) {
1832
1832
  /** Resolve to "ollama" | "openai" | "local". `kind` is the user's Settings choice
1833
1833
  * ("ollama" | "openai" | "" = Auto). Only Auto costs a probe. */
1834
1834
  async function resolveCodingBackend(kind, baseURL) {
1835
- if (kind === "ollama") return "ollama";
1836
- if (kind === "openai") return "openai";
1835
+ if (kind === "ollama" || kind === "openai" || kind === "local") return kind;
1837
1836
  if (await _probeIsOllama(baseURL)) return "ollama";
1838
1837
  return _isLocalCodingUrl(baseURL) ? "local" : "openai";
1839
1838
  }
@@ -1872,8 +1871,9 @@ async function runAgentChatJob(job) {
1872
1871
  const cancelAc = new AbortController();
1873
1872
  let cancelled = false;
1874
1873
  // Task #108: the user's Coding backend setting ("ollama" | "openai" | "" = Auto).
1875
- const codingKind =
1876
- payload?.codingKind === "ollama" || payload?.codingKind === "openai" ? payload.codingKind : "";
1874
+ const codingKind = ["ollama", "openai", "local"].includes(payload?.codingKind)
1875
+ ? payload.codingKind
1876
+ : "";
1877
1877
  const runRes = await workerFetch(`/api/worker/jobs/${jobId}`, {
1878
1878
  method: "PATCH",
1879
1879
  body: JSON.stringify({ jobStatus: "running" }),
package/gonext-repl.mjs CHANGED
@@ -876,32 +876,52 @@ async function chooseModel() {
876
876
  }
877
877
  const allowed = Array.isArray(probe?.codingAllowed) ? probe.codingAllowed : [];
878
878
  const def = (probe?.codingDefault ?? "").trim();
879
- if (allowed.length === 0) {
879
+ // Task #123: a choice is a (backend, model) PAIR — several backends (Ollama, an
880
+ // OpenAI-compatible endpoint, local MLX) can be enabled at once, and picking one moves
881
+ // the URL, the kind and the API key with it. `codingChoices` carries the backend of each
882
+ // entry so the list can say which is which; `codingAllowed` is the same ids, and stays
883
+ // the fallback for an API that predates this.
884
+ const choices = Array.isArray(probe?.codingChoices) && probe.codingChoices.length
885
+ ? probe.codingChoices
886
+ : allowed.map((id) => ({ id, model: id, kind: "" }));
887
+ if (choices.length === 0) {
880
888
  console.log(dim(" No coding model configured. Set one in the web app → Settings → Agent.\n"));
881
889
  return;
882
890
  }
883
- if (allowed.length === 1) {
891
+ if (choices.length === 1) {
884
892
  console.log(
885
- dim(` Only one coding model is available: ${allowed[0]}.\n`) +
886
- dim(" Add more in the web app → Settings → Agent → Load Models (checkboxes).\n")
893
+ dim(` Only one coding model is available: ${choices[0].model}.\n`) +
894
+ dim(" Add more in the web app → Settings → Agent → Coding backends.\n")
887
895
  );
888
896
  return;
889
897
  }
890
898
  const active = sessionCodingModel || def;
891
- // Arrow-key picker (↑/↓ + Enter) — pre-highlight the active model, tag the default.
892
- const labels = allowed.map((m) => (m === def ? `${m} (default)` : m));
893
- const activeIdx = allowed.findIndex((m) => m === active);
899
+ // Arrow-key picker (↑/↓ + Enter) — pre-highlight the active model, tag the default, and
900
+ // name the backend when more than one is in play (the same model name can appear twice).
901
+ const KIND_LABEL = { ollama: "Ollama", openai: "OpenAI-compatible", local: "local MLX", "": "auto" };
902
+ const showKind = new Set(choices.map((c) => c.kind ?? "")).size > 1;
903
+ const labels = choices.map((c) => {
904
+ const kind = showKind ? ` ${dim(`· ${KIND_LABEL[c.kind ?? ""] ?? c.kind}`)}` : "";
905
+ return `${c.model}${kind}${c.id === def ? " (default)" : ""}`;
906
+ });
907
+ const activeIdx = choices.findIndex((c) => c.id === active);
894
908
  console.log(dim(" Choose the coding model (↑/↓ then Enter):"));
895
909
  const idx = await pickFromList(labels, activeIdx >= 0 ? activeIdx : 0);
896
910
  if (idx < 0) {
897
911
  console.log(dim(" (cancelled)\n"));
898
912
  return;
899
913
  }
900
- const chosen = allowed[idx];
914
+ const chosen = choices[idx];
901
915
  // Empty override means "use the account default" — so don't send an override when the
902
916
  // user picks the default (keeps the payload clean and lets a later default change win).
903
- sessionCodingModel = chosen === def ? "" : chosen;
904
- console.log(green(` ✓ coding model ${chosen}${chosen === def ? " (default)" : ""}\n`));
917
+ sessionCodingModel = chosen.id === def ? "" : chosen.id;
918
+ // Remember the friendly name for the token footer, which should never show a raw
919
+ // "<kind>::<model>" wire id.
920
+ sessionCodingLabel = chosen.id === def ? "" : chosen.model;
921
+ const where = showKind ? ` (${KIND_LABEL[chosen.kind ?? ""] ?? chosen.kind})` : "";
922
+ console.log(
923
+ green(` ✓ coding model → ${chosen.model}${where}${chosen.id === def ? " (default)" : ""}\n`)
924
+ );
905
925
  }
906
926
 
907
927
  // Probe whether SSH KEY auth already works for a server (so we can tell the user to run
@@ -1252,7 +1272,12 @@ let jobIdForApprovalRace = "";
1252
1272
 
1253
1273
  // Per-session coding-model override chosen via /model (empty = use the account default).
1254
1274
  // Sent to /agent-ask as codingModelOverride; the API validates it against the whitelist.
1275
+ // Task #123: with several backends configured this is a QUALIFIED id ("<kind>::<model>"),
1276
+ // which resolves to a whole backend — URL, kind and API key — not just a model name.
1255
1277
  let sessionCodingModel = "";
1278
+ // The friendly model name behind sessionCodingModel, for display only: the token footer
1279
+ // must never show a raw "<kind>::<model>" wire id. Empty = running the account default.
1280
+ let sessionCodingLabel = "";
1256
1281
  // The default coding model id (from the startup agent-payload probe) — shown in the
1257
1282
  // per-turn token footer next to the GLOBAL output total so it's clear which model that
1258
1283
  // lifetime count belongs to. The /model override (sessionCodingModel) wins when set.
@@ -2383,7 +2408,7 @@ async function main() {
2383
2408
  // name so it's clear which coder that running total belongs to. Guard on THIS turn's
2384
2409
  // activity so a plain-reply greeting that did no code-model work shows nothing (#112).
2385
2410
  if ((inputTokens ?? 0) > 0 || (turnOutputTokens ?? 0) > 0) {
2386
- const coder = sessionCodingModel || defaultCodingModel;
2411
+ const coder = sessionCodingLabel || defaultCodingModel;
2387
2412
  console.log(
2388
2413
  dim("tokens · ↑ in ") +
2389
2414
  fmtTokens(inputTokens ?? 0) +
@@ -949,10 +949,8 @@ def _resolve_coding_backend(kind, base_url, probe=None):
949
949
  `probe` is injectable so the resolution table can be replayed offline without network.
950
950
  """
951
951
  k = (kind or "").strip().lower()
952
- if k == "ollama":
953
- return "ollama", "explicit setting"
954
- if k == "openai":
955
- return "openai", "explicit setting"
952
+ if k in ("ollama", "openai", "local"):
953
+ return k, "explicit setting"
956
954
  if (probe or _is_ollama_server)(base_url):
957
955
  return "ollama", "auto: server answers as Ollama"
958
956
  if not _is_local_url(base_url):
@@ -3620,7 +3618,7 @@ def run_agent_chat(cfg):
3620
3618
  # coding_api_key: the Bearer key for an OpenAI-compatible coder (empty otherwise). Used
3621
3619
  # for the code-model calls (and coding-model auto-detect), NOT the chat agent_api_key.
3622
3620
  coding_kind = (cfg.get("codingKind") or "").strip().lower()
3623
- if coding_kind not in ("ollama", "openai"):
3621
+ if coding_kind not in ("ollama", "openai", "local"):
3624
3622
  coding_kind = ""
3625
3623
  coding_api_key = (cfg.get("codingApiKey") or "").strip()
3626
3624
  # Task #107: when on, emit each code-model response so the worker persists it to Mongo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.332",
3
+ "version": "1.0.334",
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",