179 lines
5.6 KiB
JavaScript
179 lines
5.6 KiB
JavaScript
/**
|
||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||
* All rights reserved.
|
||
*
|
||
* This software is proprietary and confidential.
|
||
* Unauthorized copying, modification, or distribution is prohibited.
|
||
*
|
||
* For licensing inquiries: info@hb3-accelerator.com
|
||
* Website: https://hb3-accelerator.com
|
||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||
*/
|
||
|
||
const express = require('express');
|
||
const router = express.Router();
|
||
const { exec } = require('child_process');
|
||
const util = require('util');
|
||
const execAsync = util.promisify(exec);
|
||
const logger = require('../utils/logger');
|
||
const { requireAuth } = require('../middleware/auth');
|
||
|
||
// Проверка статуса подключения к Ollama
|
||
router.get('/status', requireAuth, async (req, res) => {
|
||
try {
|
||
const axios = require('axios');
|
||
const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://ollama:11434';
|
||
|
||
// Проверяем API Ollama через HTTP запрос
|
||
try {
|
||
const response = await axios.get(`${ollamaUrl}/api/tags`, {
|
||
timeout: 5000 // 5 секунд таймаут
|
||
});
|
||
|
||
const models = response.data.models || [];
|
||
return res.json({
|
||
connected: true,
|
||
message: 'Ollama is running',
|
||
models: models.length,
|
||
availableModels: models.map(m => m.name)
|
||
});
|
||
} catch (apiError) {
|
||
logger.error('Ollama API error:', apiError.message);
|
||
return res.json({
|
||
connected: false,
|
||
error: `Ollama API not responding: ${apiError.message}`
|
||
});
|
||
}
|
||
} catch (error) {
|
||
logger.error('Error checking Ollama status:', error);
|
||
res.status(500).json({ connected: false, error: 'Failed to check Ollama status' });
|
||
}
|
||
});
|
||
|
||
// Получение списка установленных моделей
|
||
router.get('/models', requireAuth, async (req, res) => {
|
||
try {
|
||
const axios = require('axios');
|
||
const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://ollama:11434';
|
||
|
||
const response = await axios.get(`${ollamaUrl}/api/tags`, {
|
||
timeout: 5000
|
||
});
|
||
|
||
const models = (response.data.models || []).map(model => ({
|
||
name: model.name,
|
||
id: model.model || model.name,
|
||
size: model.size || 0,
|
||
modified: model.modified_at || 'Unknown'
|
||
}));
|
||
|
||
res.json({ models });
|
||
} catch (error) {
|
||
logger.error('Error getting Ollama models:', error);
|
||
res.status(500).json({ error: 'Failed to get models' });
|
||
}
|
||
});
|
||
|
||
// Установка модели
|
||
router.post('/install', requireAuth, async (req, res) => {
|
||
const { model } = req.body;
|
||
|
||
if (!model) {
|
||
return res.status(400).json({ error: 'Model name is required' });
|
||
}
|
||
|
||
try {
|
||
logger.info(`Starting installation of model: ${model}`);
|
||
|
||
// Запускаем установку в фоне
|
||
const installProcess = exec(`docker exec dapp-ollama ollama pull ${model}`, (error, stdout, stderr) => {
|
||
if (error) {
|
||
logger.error(`Error installing model ${model}:`, error);
|
||
} else {
|
||
logger.info(`Successfully installed model: ${model}`);
|
||
}
|
||
});
|
||
|
||
// Возвращаем ответ сразу, не ждем завершения
|
||
res.json({
|
||
success: true,
|
||
message: `Installation of ${model} started`,
|
||
processId: installProcess.pid
|
||
});
|
||
} catch (error) {
|
||
logger.error('Error starting model installation:', error);
|
||
res.status(500).json({ error: 'Failed to start installation' });
|
||
}
|
||
});
|
||
|
||
// Удаление модели
|
||
router.delete('/models/:modelName', requireAuth, async (req, res) => {
|
||
const { modelName } = req.params;
|
||
|
||
if (!modelName) {
|
||
return res.status(400).json({ error: 'Model name is required' });
|
||
}
|
||
|
||
try {
|
||
logger.info(`Removing model: ${modelName}`);
|
||
|
||
const { stdout, stderr } = await execAsync(`docker exec dapp-ollama ollama rm ${modelName}`);
|
||
|
||
if (stderr && !stderr.includes('deleted')) {
|
||
throw new Error(stderr);
|
||
}
|
||
|
||
logger.info(`Successfully removed model: ${modelName}`);
|
||
res.json({ success: true, message: `Model ${modelName} removed successfully` });
|
||
} catch (error) {
|
||
logger.error(`Error removing model ${modelName}:`, error);
|
||
res.status(500).json({ error: `Failed to remove model: ${error.message}` });
|
||
}
|
||
});
|
||
|
||
// Получение информации о модели
|
||
router.get('/models/:modelName', requireAuth, async (req, res) => {
|
||
const { modelName } = req.params;
|
||
|
||
try {
|
||
const { stdout } = await execAsync(`docker exec dapp-ollama ollama show ${modelName}`);
|
||
res.json({ model: modelName, info: stdout });
|
||
} catch (error) {
|
||
logger.error(`Error getting model info for ${modelName}:`, error);
|
||
res.status(404).json({ error: 'Model not found' });
|
||
}
|
||
});
|
||
|
||
// Поиск моделей в реестре (если поддерживается)
|
||
router.get('/search', requireAuth, async (req, res) => {
|
||
const { query } = req.query;
|
||
|
||
if (!query) {
|
||
return res.status(400).json({ error: 'Search query is required' });
|
||
}
|
||
|
||
try {
|
||
// Пока просто возвращаем популярные модели
|
||
const popularModels = [
|
||
'qwen2.5:7b',
|
||
'llama2:7b',
|
||
'mistral:7b',
|
||
'codellama:7b',
|
||
'llama2:13b',
|
||
'qwen2.5:14b',
|
||
'gemma:7b',
|
||
'phi3:3.8b'
|
||
];
|
||
|
||
const filteredModels = popularModels.filter(model =>
|
||
model.toLowerCase().includes(query.toLowerCase())
|
||
);
|
||
|
||
res.json({ models: filteredModels });
|
||
} catch (error) {
|
||
logger.error('Error searching models:', error);
|
||
res.status(500).json({ error: 'Failed to search models' });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|