import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; import { getLlama, LlamaChatSession } from 'node-llama-cpp'; // Определяем пути // __dirname теперь указывает на папку server const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Поднимаемся на уровень выше, чтобы попасть в корень проекта const PROJECT_ROOT = path.join(__dirname, '..'); // Настраиваем dotenv явно, чтобы он нашел файл .env в корне dotenv.config({ path: path.join(PROJECT_ROOT, '.env') }); const app = express(); const PORT = process.env.PORT || 3000; app.use(cors()); app.use(express.json()); // Статику (HTML, CSS, JS) теперь берем из папки public в корне app.use(express.static(path.join(PROJECT_ROOT, 'public'))); let llama = null; let model = null; let context = null; // --- Инициализация Сервера --- console.log("🌐 Запуск веб-сервера..."); async function initServer() { try { llama = await getLlama(); console.log("✅ Движок Llama инициализирован (папка server/)."); console.log("⚠️ Модель НЕ загружена. Ожидание команды из интерфейса..."); } catch (err) { console.error("❌ Критическая ошибка инициализации движка:", err.message); process.exit(1); } } initServer(); // --- API Управления Моделью --- // 1. Загрузка модели app.post('/api/model/load', async (req, res) => { const { path: inputPath } = req.body; if (!inputPath) { return res.status(400).json({ success: false, message: 'Путь к модели не указан' }); } if (model) { return res.status(400).json({ success: false, message: 'Модель уже загружена.' }); } // Логика путей: // Если путь относительный (начинается с ./ или имени файла), склеиваем с КОРНЕМ ПРОЕКТА. // Если абсолютный — оставляем как есть. let absolutePath; if (path.isAbsolute(inputPath)) { absolutePath = inputPath; } else { absolutePath = path.join(PROJECT_ROOT, inputPath); } try { console.log(`🔄 [ЗАГРУЗКА] Попытка загрузки модели из: ${absolutePath}`); model = await llama.loadModel({ modelPath: absolutePath }); context = await model.createContext({ contextSize: 2048 }); console.log("✅ [УСПЕХ] Модель успешно загружена."); res.json({ success: true, message: `Модель загружена` }); } catch (err) { console.error(`❌ [ОШИБКА] Не удалось загрузить модель: ${err.message}`); model = null; context = null; res.status(500).json({ success: false, message: err.message }); } }); // 2. Выгрузка модели app.post('/api/model/unload', async (req, res) => { if (!model) { return res.status(400).json({ success: false, message: 'Нет активной модели.' }); } try { console.log("🔄 [ВЫГРУЗКА] Освобождение ресурсов модели..."); if (context) context.dispose(); if (model) model.dispose(); model = null; context = null; console.log("✅ [УСПЕХ] Модель выгружена."); res.json({ success: true, message: "Модель выгружена." }); } catch (err) { res.status(500).json({ success: false, message: err.message }); } }); // 3. Остановка сервера app.post('/api/server/stop', (req, res) => { console.log("🛑 [СЕРВЕР] Остановка..."); res.json({ success: true, message: "Остановка..." }); setTimeout(() => process.exit(0), 500); }); // --- API Автодополнения --- // Считывает папку model из КОРНЯ ПРОЕКТА app.get('/api/models/list', async (req, res) => { const modelDir = path.join(PROJECT_ROOT, 'model'); try { await fs.promises.access(modelDir); const files = await fs.promises.readdir(modelDir); const ggufModels = files.filter(file => file.endsWith('.gguf')).sort(); res.json(ggufModels); } catch (err) { console.log("⚠️ Папка 'model' не найдена в корне проекта."); res.json([]); } }); // --- API Чата --- app.post('/api/chat', async (req, res) => { if (!model || !context) { return res.status(503).send("Модель не загружена."); } const { message } = req.body; if (!message) return res.status(400).json({ error: 'Message is required' }); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Transfer-Encoding', 'chunked'); let sequence; try { sequence = context.getSequence(); const session = new LlamaChatSession({ contextSequence: sequence }); await session.prompt(message, { onToken: (token) => { try { const chunk = model.detokenize(token); res.write(chunk); } catch (decodeErr) { console.error("Ошибка декодирования:", decodeErr); } }, temperature: 0.7, maxTokens: 2000, }); res.end(); } catch (err) { console.error("Ошибка генерации:", err.message); res.write("\n\n[Ошибка генерации]"); res.end(); } finally { if (sequence) sequence.dispose(); } }); app.listen(PORT, () => { console.log(`✅ [СЕРВЕР] Веб-сервер запущен на порту: www.localhost:${PORT}`); });