fix: Server-Auto-Start im CLI — kein manueller Vorstart nötig
- CLI startet llama-server automatisch bei fehlender Verbindung - Modellauswahl per Ziffer (Default=3=kleinstes) - Live-Logs während Server-Start - Timeout 120s für große Modelle vom USB-Stick - Kein interaktives start-server.sh mehr nötig
This commit is contained in:
parent
da86c2d83b
commit
b9dadb479a
1 changed files with 145 additions and 38 deletions
183
cli/crowdcode.js
183
cli/crowdcode.js
|
|
@ -295,7 +295,7 @@ async function applyActions(actions) {
|
|||
case 'bash': {
|
||||
console.log(` ${C.cyan}⚡${C.reset} Bash-Befehl erkannt:`);
|
||||
console.log(` ${C.grey}${a.command}${C.reset}`);
|
||||
const confirm = await ask(` ${C.yellow}Ausführen?${C.reset} (j/N) `);
|
||||
const confirm = await askSingleLine(` ${C.yellow}Ausführen?${C.reset} (j/N) `);
|
||||
if (confirm.toLowerCase() === 'j' || confirm.toLowerCase() === 'ja') {
|
||||
try {
|
||||
const out = execSync(a.command, { cwd: WORKSPACE, timeout: 30000, encoding: 'utf8', maxBuffer: 1024 * 512 });
|
||||
|
|
@ -314,19 +314,119 @@ async function applyActions(actions) {
|
|||
}
|
||||
|
||||
// ─── Modelle aus dem MiniLlama/models-Ordner anzeigen ─────────────────────────
|
||||
function showAvailableModels(modelsDir) {
|
||||
function getLocalModels() {
|
||||
const modelsDir = path.join(__dirname, '..', 'models');
|
||||
if (!fs.existsSync(modelsDir)) return [];
|
||||
const files = fs.readdirSync(modelsDir).filter(f => f.endsWith('.gguf'));
|
||||
if (files.length === 0) return [];
|
||||
return fs.readdirSync(modelsDir)
|
||||
.filter(f => f.endsWith('.gguf'))
|
||||
.sort()
|
||||
.map((name, i) => {
|
||||
const fullPath = path.join(modelsDir, name);
|
||||
try {
|
||||
const size = (fs.statSync(fullPath).size / 1024 / 1024 / 1024).toFixed(1);
|
||||
return { index: i + 1, name, path: fullPath, size };
|
||||
} catch { return { index: i + 1, name, path: fullPath, size: '?' }; }
|
||||
});
|
||||
}
|
||||
|
||||
function showLocalModels(models) {
|
||||
if (models.length === 0) return;
|
||||
console.log(`\n${C.bold}📦 Verfügbare Modelle:${C.reset}`);
|
||||
files.forEach((f, i) => {
|
||||
try {
|
||||
const stat = fs.statSync(path.join(modelsDir, f));
|
||||
const size = (stat.size / 1024 / 1024 / 1024).toFixed(1);
|
||||
console.log(` ${C.cyan}${i + 1})${C.reset} ${f} ${C.grey}(${size} GB)${C.reset}`);
|
||||
} catch { console.log(` ${C.cyan}${i + 1})${C.reset} ${f}`); }
|
||||
models.forEach(m => {
|
||||
console.log(` ${C.cyan}${m.index})${C.reset} ${m.name} ${C.grey}(${m.size} GB)${C.reset}`);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Server starten ───────────────────────────────────────────────────────────
|
||||
function findLlamaServer() {
|
||||
// Pfade für llama-server Binary suchen
|
||||
const candidates = [
|
||||
path.join(__dirname, '..', 'bin', 'linux', 'llama-server'),
|
||||
path.join(__dirname, '..', 'bin', 'win', 'llama-server.exe'),
|
||||
];
|
||||
for (const p of candidates) {
|
||||
if (fs.existsSync(p)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function startServerWithModel(localModels) {
|
||||
showLocalModels(localModels);
|
||||
if (localModels.length === 0) {
|
||||
console.log(`\n${C.red}❌ Keine .gguf-Modelle im MiniLlama/models/-Ordner gefunden.${C.reset}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// User wählt Modell per Ziffer
|
||||
let modelPath = null;
|
||||
while (!modelPath) {
|
||||
const input = await askSingleLine(`\n${C.yellow}🔌 Server starten mit Modell Nr.${C.reset} [1-${localModels.length}] ${C.dim}(Enter=3=kleinstes)${C.reset}: `);
|
||||
const choice = input ? parseInt(input) : 3;
|
||||
if (!isNaN(choice) && choice >= 1 && choice <= localModels.length) {
|
||||
modelPath = localModels[choice - 1];
|
||||
} else {
|
||||
console.log(` ${C.red}Bitte eine Zahl zwischen 1 und ${localModels.length}${C.reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
const serverBin = findLlamaServer();
|
||||
if (!serverBin) {
|
||||
console.log(`\n${C.red}❌ Kein llama-server Binary gefunden.${C.reset}`);
|
||||
console.log(` ${C.dim}Erwartet in bin/linux/llama-server oder bin/win/llama-server.exe${C.reset}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`\n${C.dim}🚀 Starte Server mit ${modelPath.name}...${C.reset}\n`);
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const MINILLAMA_DIR = path.join(__dirname, '..');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = spawn(serverBin, [
|
||||
'-m', modelPath.path,
|
||||
'--host', '127.0.0.1',
|
||||
'--port', '8080',
|
||||
'--ctx-size', '4096',
|
||||
'--n-gpu-layers', '0',
|
||||
], {
|
||||
cwd: MINILLAMA_DIR,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
let started = false;
|
||||
|
||||
server.stdout.on('data', (data) => {
|
||||
const text = data.toString();
|
||||
process.stdout.write(text); // Live-Logs zeigen
|
||||
if (!started && (text.includes('model loaded') || text.includes('listening on') || text.includes('start'))) {
|
||||
started = true;
|
||||
// Kurz warten bis Server wirklich bereit
|
||||
setTimeout(() => resolve(server), 500);
|
||||
}
|
||||
});
|
||||
|
||||
server.stderr.on('data', (data) => {
|
||||
const text = data.toString();
|
||||
process.stdout.write(text); // Live-Logs zeigen
|
||||
if (!started && (text.includes('model loaded') || text.includes('listening on') || text.includes('start')) && !text.includes('error')) {
|
||||
started = true;
|
||||
setTimeout(() => resolve(server), 500);
|
||||
}
|
||||
});
|
||||
|
||||
server.on('error', (err) => {
|
||||
reject(new Error(`Server-Start fehlgeschlagen: ${err.message}`));
|
||||
});
|
||||
|
||||
server.on('exit', (code) => {
|
||||
if (!started) reject(new Error(`Server beendet mit Code ${code}`));
|
||||
});
|
||||
|
||||
// Timeout: Wenn Server nicht innerhalb von 120s startet (große Modelle vom Stick)
|
||||
setTimeout(() => {
|
||||
if (!started) reject(new Error('Timeout: Server nicht innerhalb von 120s gestartet. Möglicherweise zu großes Modell oder USB-Stick zu langsam.'));
|
||||
}, 120000);
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
// ─── Hauptprogramm ────────────────────────────────────────────────────────────
|
||||
|
|
@ -337,43 +437,50 @@ async function main() {
|
|||
console.log(`${C.bold}${C.cyan}╚══════════════════════════════════════════╝${C.reset}`);
|
||||
console.log(`${C.grey} API: ${API} | Workspace: ${WORKSPACE}${C.reset}\n`);
|
||||
|
||||
// Verfügbare Modelle prüfen
|
||||
const modelsDir = path.join(__dirname, '..', 'models');
|
||||
const localModels = showAvailableModels(modelsDir);
|
||||
// Verfügbare Modelle anzeigen
|
||||
const localModels = getLocalModels();
|
||||
|
||||
// API-Verbindung testen
|
||||
console.log(`\n${C.dim}🔌 Teste Verbindung zu ${API}...${C.reset}`);
|
||||
// Lokale Modelle IMMER anzeigen (auch wenn Server schon läuft)
|
||||
if (localModels.length > 0) {
|
||||
console.log(`${C.bold}📦 Verfügbare lokale Modelle:${C.reset}`);
|
||||
localModels.forEach(m => {
|
||||
console.log(` ${C.cyan}${m.index})${C.reset} ${m.name} ${C.grey}(${m.size} GB)${C.reset}`);
|
||||
});
|
||||
}
|
||||
|
||||
// API-Verbindung testen → falls Server schon läuft, direkt loslegen
|
||||
let serverProcess = null;
|
||||
let connected = false;
|
||||
console.log(`\n${C.dim}🔌 Teste Verbindung zu ${API}...${C.reset}`);
|
||||
try {
|
||||
const models = await fetchModels();
|
||||
if (models.length > 0) {
|
||||
const modelName = MODEL || models[0]?.id || models[0]?.name || models[0]?.model || '(unbekannt)';
|
||||
console.log(` ${C.green}✅${C.reset} Verbunden. Modell: ${C.bold}${modelName}${C.reset}`);
|
||||
console.log(` ${C.green}✅${C.reset} Server läuft bereits. Modell: ${C.bold}${modelName}${C.reset}`);
|
||||
connected = true;
|
||||
} else {
|
||||
console.log(` ${C.yellow}⚠️${C.reset} Server verbunden, aber keine Modelle geladen.`);
|
||||
console.log(` ${C.dim} Stelle sicher, dass ein Modell geladen ist.${C.reset}`);
|
||||
connected = true; // Server läuft trotzdem
|
||||
console.log(` ${C.yellow}⚠️${C.reset} Server antwortet, aber kein Modell geladen.`);
|
||||
console.log(` ${C.dim} → Starte Server neu mit einem lokalen Modell.${C.reset}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` ${C.red}❌${C.reset} Keine Verbindung zu ${API}`);
|
||||
console.log(` ${C.dim} Starte zuerst den Server: ./start-server.sh${C.reset}`);
|
||||
console.log(` ${C.dim} Oder: ./bin/linux/llama-server -m models/<modell>.gguf --host 127.0.0.1 --port 8080${C.reset}`);
|
||||
if (localModels.length > 0) {
|
||||
const ans = await ask(` ${C.yellow}Server lokal starten?${C.reset} (j/N) `);
|
||||
if (ans.toLowerCase() === 'j' || ans.toLowerCase() === 'ja') {
|
||||
const serverPath = path.join(__dirname, '..', 'start-server.sh');
|
||||
if (fs.existsSync(serverPath)) {
|
||||
const child = require('child_process').spawn(serverPath, [], { stdio: 'inherit', cwd: path.join(__dirname, '..') });
|
||||
console.log(` ${C.dim}Server gestartet, drücke Enter wenn bereit...${C.reset}`);
|
||||
await ask('');
|
||||
// Erneut testen
|
||||
try { await fetchModels(); connected = true; } catch { }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.log(` ${C.red}❌${C.reset} Kein Server an ${API}`);
|
||||
console.log(` ${C.dim} → Starte Server automatisch mit lokalem Modell.${C.reset}`);
|
||||
}
|
||||
|
||||
// Server starten, falls nicht verbunden
|
||||
if (!connected) {
|
||||
if (localModels.length === 0) {
|
||||
console.log(`\n${C.red}❌ Keine .gguf-Modelle gefunden.${C.reset}`);
|
||||
console.log(` ${C.dim}Lege Modelle in models/ ab oder starte llama-server manuell.${C.reset}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (!connected) {
|
||||
console.log(`\n${C.red}Bitte zuerst llama-server starten.${C.reset}`);
|
||||
|
||||
try {
|
||||
serverProcess = await startServerWithModel(localModels);
|
||||
connected = true;
|
||||
console.log(`\n${C.green}✅ Server bereit.${C.reset}`);
|
||||
} catch (err) {
|
||||
console.log(`\n${C.red}❌ Server-Start fehlgeschlagen: ${err.message}${C.reset}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue