@krmxd/onegpt 0.1.8-beta → 0.1.9-beta
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/bin/ogpt.js +20 -2
- package/package.json +1 -1
- package/src/ollama-setup.js +141 -0
- package/src/ollama.js +20 -2
- package/src/web.js +3 -2
package/bin/ogpt.js
CHANGED
|
@@ -99,13 +99,14 @@ async function main() {
|
|
|
99
99
|
let sessionId = "";
|
|
100
100
|
|
|
101
101
|
for (let i = 0; i < args.length; i++) {
|
|
102
|
-
if (args[i] === "--no-stream"
|
|
102
|
+
if (args[i] === "--no-stream") {
|
|
103
103
|
noStream = true;
|
|
104
104
|
} else if (args[i] === "--auto") {
|
|
105
105
|
autoApprove = true;
|
|
106
106
|
} else if (args[i] === "--continue" || args[i] === "-c") {
|
|
107
107
|
continueLast = true;
|
|
108
|
-
} else if (args[i] === "--session") {
|
|
108
|
+
} else if (args[i] === "--session" || args[i] === "-s") {
|
|
109
|
+
// -s <id> = resume session, matching the Python build's argparse
|
|
109
110
|
sessionId = args[++i] || "";
|
|
110
111
|
} else if (args[i] === "--model" || args[i] === "-m") {
|
|
111
112
|
const name = args[++i];
|
|
@@ -132,6 +133,23 @@ Options:
|
|
|
132
133
|
|
|
133
134
|
if (noStream) cfg.set("ui.stream", false);
|
|
134
135
|
|
|
136
|
+
// Local-engine bootstrap: install/start the engine and make sure a model
|
|
137
|
+
// exists BEFORE chatting, so users never see raw ECONNREFUSED errors.
|
|
138
|
+
if ((cfg.get("active_provider", "ollama") || "ollama") === "ollama"
|
|
139
|
+
&& args[0] !== "--help") {
|
|
140
|
+
const { ensureLocalEngine } = require("../src/ollama-setup");
|
|
141
|
+
const res = await ensureLocalEngine({
|
|
142
|
+
host: cfg.get("providers.ollama.host"),
|
|
143
|
+
model: cfg.activeModel ? cfg.activeModel() : cfg.get("active_model"),
|
|
144
|
+
});
|
|
145
|
+
if (res.ok && res.model && res.model !== cfg.get("active_model")) {
|
|
146
|
+
cfg.set("active_model", res.model);
|
|
147
|
+
}
|
|
148
|
+
if (!res.ok && res.reason) {
|
|
149
|
+
console.log("\n" + res.reason + "\n");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
135
153
|
const cli = new CLI({ config: cfg });
|
|
136
154
|
|
|
137
155
|
if (autoApprove) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Local-engine bootstrap (parity with ogpt/utils/ollama_setup.py).
|
|
4
|
+
// Makes `ogpt` work right after install: installs the engine if missing,
|
|
5
|
+
// starts the server daemon, picks a model that fits the RAM, and pulls it -
|
|
6
|
+
// all installer output hidden behind short status lines.
|
|
7
|
+
|
|
8
|
+
const { spawn, spawnSync } = require("child_process");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
|
|
11
|
+
function isInstalled() {
|
|
12
|
+
try {
|
|
13
|
+
return spawnSync("ollama", ["--version"],
|
|
14
|
+
{ stdio: "ignore", timeout: 15000 }).status === 0;
|
|
15
|
+
} catch { return false; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _getJson(host, path, timeoutMs) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const uri = new URL(path, host);
|
|
21
|
+
const mod = uri.protocol === "https:" ? require("https") : require("http");
|
|
22
|
+
const req = mod.request(uri, { timeout: timeoutMs }, (res) => {
|
|
23
|
+
let data = "";
|
|
24
|
+
res.on("data", (c) => { data += c; });
|
|
25
|
+
res.on("end", () => {
|
|
26
|
+
try { resolve({ ok: res.statusCode === 200, json: JSON.parse(data) }); }
|
|
27
|
+
catch { resolve({ ok: false }); }
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
req.on("timeout", () => { req.destroy(); resolve({ ok: false }); });
|
|
31
|
+
req.on("error", () => resolve({ ok: false }));
|
|
32
|
+
req.end();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function serverUp(host, timeoutMs = 2500) {
|
|
37
|
+
const r = await _getJson(host, "/api/tags", timeoutMs);
|
|
38
|
+
return r.ok;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function startServer(host, waitSec = 20) {
|
|
42
|
+
try {
|
|
43
|
+
const child = spawn("ollama", ["serve"],
|
|
44
|
+
{ stdio: "ignore", detached: true });
|
|
45
|
+
child.unref();
|
|
46
|
+
} catch { return false; }
|
|
47
|
+
const deadline = Date.now() + waitSec * 1000;
|
|
48
|
+
while (Date.now() < deadline) {
|
|
49
|
+
await sleep(500);
|
|
50
|
+
if (await serverUp(host)) return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
|
|
56
|
+
|
|
57
|
+
async function listModels(host) {
|
|
58
|
+
const r = await _getJson(host, "/api/tags", 4000);
|
|
59
|
+
if (!r.ok || !r.json || !Array.isArray(r.json.models)) return [];
|
|
60
|
+
return r.json.models.map((m) => m.name || "").filter(Boolean);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function pullModel(host, model, timeoutSec = 1800) {
|
|
64
|
+
console.log("Installing packages...");
|
|
65
|
+
try {
|
|
66
|
+
spawnSync("ollama", ["pull", model],
|
|
67
|
+
{ stdio: "ignore", timeout: timeoutSec * 1000 });
|
|
68
|
+
} catch {}
|
|
69
|
+
const installed = await listModels(host);
|
|
70
|
+
const base = model.split(":")[0];
|
|
71
|
+
return installed.includes(model)
|
|
72
|
+
|| installed.some((m) => m.startsWith(base + ":"));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function ramGB() {
|
|
76
|
+
return Math.round(os.totalmem() / (1024 * 1024 * 1024));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function pickModel(host, configuredModel) {
|
|
80
|
+
const installed = await listModels(host);
|
|
81
|
+
if (installed.includes(configuredModel)) return configuredModel;
|
|
82
|
+
let fit = null;
|
|
83
|
+
try {
|
|
84
|
+
const { recommendForRam } = require("./catalog");
|
|
85
|
+
fit = recommendForRam(ramGB());
|
|
86
|
+
} catch {}
|
|
87
|
+
if (fit && installed.includes(fit)) return fit;
|
|
88
|
+
if (fit && !installed.length) return fit; // fresh machine: pull best fit
|
|
89
|
+
if (installed.length) return installed[0]; // something already there
|
|
90
|
+
return fit || configuredModel;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Ensure the local engine is installed, running and has a usable model.
|
|
95
|
+
* Returns {ok, model, reason} - never throws.
|
|
96
|
+
*/
|
|
97
|
+
async function ensureLocalEngine({ host, model } = {}) {
|
|
98
|
+
host = host || "http://127.0.0.1:11434";
|
|
99
|
+
model = model || "qwen2.5-coder:1.5b";
|
|
100
|
+
try {
|
|
101
|
+
if (!isInstalled()) {
|
|
102
|
+
console.log("Installing packages...");
|
|
103
|
+
const { installOllama } = require("./bootstrap");
|
|
104
|
+
if (!installOllama()) {
|
|
105
|
+
return { ok: false, reason:
|
|
106
|
+
"Local engine could not be installed automatically. "
|
|
107
|
+
+ "Grab it from https://ollama.com/download, or pick another "
|
|
108
|
+
+ "provider with /provider." };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
let up = await serverUp(host);
|
|
112
|
+
if (!up) {
|
|
113
|
+
console.log("Starting local engine...");
|
|
114
|
+
up = await startServer(host);
|
|
115
|
+
if (!up) {
|
|
116
|
+
return { ok: false, reason:
|
|
117
|
+
`Local engine did not respond on ${host} within 20s. `
|
|
118
|
+
+ "Try 'ollama serve' manually or check 'journalctl -u ollama'." };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const chosen = await pickModel(host, model);
|
|
122
|
+
const installed = await listModels(host);
|
|
123
|
+
const base = chosen.split(":")[0];
|
|
124
|
+
const have = installed.includes(chosen)
|
|
125
|
+
|| installed.some((m) => m.startsWith(base + ":"));
|
|
126
|
+
if (!have) {
|
|
127
|
+
const okPull = await pullModel(host, chosen);
|
|
128
|
+
if (!okPull) {
|
|
129
|
+
return { ok: false, reason:
|
|
130
|
+
`Model ${chosen} could not be downloaded (offline?). `
|
|
131
|
+
+ "Check your connection or /model to pick an installed one." };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { ok: true, model: chosen };
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return { ok: false, reason: e.message };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { ensureLocalEngine, serverUp, startServer, listModels,
|
|
141
|
+
pullModel, pickModel, isInstalled };
|
package/src/ollama.js
CHANGED
|
@@ -348,7 +348,16 @@ class OllamaProvider {
|
|
|
348
348
|
});
|
|
349
349
|
}
|
|
350
350
|
);
|
|
351
|
-
req.on("error",
|
|
351
|
+
req.on("error", (e) => {
|
|
352
|
+
if (e && (e.code === "ECONNREFUSED" || /ECONNREFUSED/.test(e.message || ""))) {
|
|
353
|
+
reject(new Error(
|
|
354
|
+
`Local engine is not running at ${this.host}. `
|
|
355
|
+
+ "Start it with 'ollama serve' (OGPT normally does this "
|
|
356
|
+
+ "automatically at launch), or switch providers with /provider."));
|
|
357
|
+
} else {
|
|
358
|
+
reject(e);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
352
361
|
req.on("timeout", () => { req.destroy(); reject(new Error("Timeout")); });
|
|
353
362
|
if (body) req.write(JSON.stringify(body));
|
|
354
363
|
req.end();
|
|
@@ -394,7 +403,16 @@ class OllamaProvider {
|
|
|
394
403
|
req.destroy(new Error(`No data from the local engine for ${Math.round(this.idleTimeout / 1000)}s`));
|
|
395
404
|
}, this.idleTimeout);
|
|
396
405
|
};
|
|
397
|
-
req.on("error", (e) => {
|
|
406
|
+
req.on("error", (e) => {
|
|
407
|
+
clearIdle();
|
|
408
|
+
if (e && e.code === "ECONNREFUSED") {
|
|
409
|
+
reject(new Error(
|
|
410
|
+
`Local engine is not running at ${this.host}. `
|
|
411
|
+
+ "Start it with 'ollama serve' or switch providers with /provider."));
|
|
412
|
+
} else {
|
|
413
|
+
reject(e);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
398
416
|
req.on("close", clearIdle);
|
|
399
417
|
req.write(JSON.stringify(body));
|
|
400
418
|
req.end();
|
package/src/web.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Same endpoints, same single-file UI, zero npm dependencies: served with
|
|
3
3
|
// the plain http module and wired into the SAME agent singleton the
|
|
4
4
|
// terminal uses, so dashboard and TUI share one brain and one counter.
|
|
5
|
+
const _PKG_VER = require("../package.json").version;
|
|
5
6
|
const fs = require("fs");
|
|
6
7
|
const os = require("os");
|
|
7
8
|
const path = require("path");
|
|
@@ -111,7 +112,7 @@ function createDashboard(cli) {
|
|
|
111
112
|
return json(res, 200, {
|
|
112
113
|
ok: true,
|
|
113
114
|
app: "OGPT",
|
|
114
|
-
version:
|
|
115
|
+
version: `${_PKG_VER}(devices-all)`,
|
|
115
116
|
model: name,
|
|
116
117
|
provider: "Ollama",
|
|
117
118
|
uptime_s: Math.round((Date.now() - STARTED_AT) / 100) / 10,
|
|
@@ -140,7 +141,7 @@ function createDashboard(cli) {
|
|
|
140
141
|
} catch {}
|
|
141
142
|
return json(res, 200, {
|
|
142
143
|
ok: true,
|
|
143
|
-
version:
|
|
144
|
+
version: `${_PKG_VER}(devices-all)`,
|
|
144
145
|
model: name,
|
|
145
146
|
model_id: name,
|
|
146
147
|
provider: "Ollama",
|