-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirstRunSetup.js
More file actions
159 lines (137 loc) · 5.01 KB
/
firstRunSetup.js
File metadata and controls
159 lines (137 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
* guIDE 2.0 — First Run Setup
*
* Detects whether this is the first launch. Gathers system info
* (GPU, VRAM, RAM, OS, CPU) to guide the onboarding wizard.
* Marks setup as complete in settingsManager so it only runs once.
*/
'use strict';
const os = require('os');
const { execSync } = require('child_process');
class FirstRunSetup {
/**
* @param {import('./settingsManager').SettingsManager} settingsManager
*/
constructor(settingsManager) {
this._settingsManager = settingsManager;
this._systemInfo = null;
}
/** True if the user has never completed the onboarding wizard. */
isFirstRun() {
return !this._settingsManager.get('setupCompleted');
}
/** Mark onboarding as done. */
markComplete() {
this._settingsManager.set('setupCompleted', true);
}
/**
* Detect GPU, VRAM, RAM, OS, and CPU info.
* Cached after first call.
* @returns {{ gpu: string, vramMB: number, ramGB: number, os: string, arch: string, cpuModel: string, cpuCores: number }}
*/
getSystemInfo() {
if (this._systemInfo) return this._systemInfo;
const ramGB = Math.round(os.totalmem() / (1024 ** 3));
const cpus = os.cpus();
const cpuModel = cpus.length > 0 ? cpus[0].model.trim() : 'Unknown';
const cpuCores = cpus.length;
let gpu = 'None detected';
let vramMB = 0;
// Try NVIDIA first
try {
const out = execSync(
'nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits',
{ timeout: 5000, windowsHide: true }
).toString().trim();
const parts = out.split('\n')[0].split(',').map(s => s.trim());
if (parts.length >= 2) {
gpu = parts[0];
vramMB = parseInt(parts[1], 10) || 0;
}
} catch {
// No NVIDIA GPU or driver not installed
}
this._systemInfo = {
gpu,
vramMB,
ramGB,
os: `${os.type()} ${os.release()}`,
arch: os.arch(),
cpuModel,
cpuCores,
};
return this._systemInfo;
}
/**
* Recommend initial settings based on system capabilities.
* @returns {{ gpuLayers: number, contextSize: number, maxModelGB: number, recommendation: string }}
*/
recommendSettings() {
const info = this.getSystemInfo();
const vramGB = info.vramMB / 1024;
let gpuLayers = -1; // -1 = auto (let node-llama-cpp decide)
/** 0 = auto (maximize to model + memory); CPU-only keeps a modest fixed window */
let contextSize = 0;
let recommendation = '';
if (info.vramMB === 0) {
// CPU-only — context stays 0 (auto); runtime picks train/hardware caps like GPU mode
gpuLayers = 0;
contextSize = 0;
recommendation = 'No GPU detected. Using CPU inference. Context defaults to auto (as large as the model and RAM allow). A 0.6B–1.7B model is recommended.';
} else if (vramGB < 4) {
recommendation = `${info.gpu} with ${Math.round(vramGB)}GB VRAM. A 0.6B-1.7B Q8 model fits well. Context defaults to auto (as large as the model and VRAM allow).`;
} else if (vramGB < 8) {
recommendation = `${info.gpu} with ${Math.round(vramGB)}GB VRAM. A 4B Q8 or 8B Q4 model is recommended.`;
} else if (vramGB < 16) {
recommendation = `${info.gpu} with ${Math.round(vramGB)}GB VRAM. An 8B-14B model works well.`;
} else if (vramGB < 32) {
recommendation = `${info.gpu} with ${Math.round(vramGB)}GB VRAM. A 14B-32B model is recommended.`;
} else {
recommendation = `${info.gpu} with ${Math.round(vramGB)}GB VRAM. Large models (32B+) are available.`;
}
const maxModelGB = info.vramMB > 0 ? Math.floor((info.vramMB * 0.85) / 1024) : 4;
return { gpuLayers, contextSize, maxModelGB, recommendation };
}
/**
* Apply recommended settings to the settings manager.
* Called when user clicks "Use recommended" in the wizard.
*/
applyRecommended() {
const rec = this.recommendSettings();
this._settingsManager.set('gpuLayers', rec.gpuLayers);
this._settingsManager.set('contextSize', rec.contextSize);
}
/**
* Register API routes on the Express app.
* @param {import('express').Application} app
*/
registerRoutes(app) {
// Get first-run status + system info
app.get('/api/setup/status', (req, res) => {
res.json({
isFirstRun: this.isFirstRun(),
systemInfo: this.getSystemInfo(),
recommended: this.recommendSettings(),
});
});
// Mark setup as complete (optionally apply settings)
app.post('/api/setup/complete', (req, res) => {
const { applyRecommended, settings } = req.body || {};
if (applyRecommended) {
this.applyRecommended();
}
// Apply any explicit settings the wizard collected
if (settings && typeof settings === 'object') {
for (const [key, value] of Object.entries(settings)) {
// Only allow known setting keys
if (key in this._settingsManager.getAll()) {
this._settingsManager.set(key, value);
}
}
}
this.markComplete();
res.json({ success: true });
});
}
}
module.exports = { FirstRunSetup };