Files
DLE/backend/routes/ollama.js
2025-10-09 16:48:20 +03:00

182 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 axios = require('axios');
const logger = require('../utils/logger');
const { requireAuth } = require('../middleware/auth');
const ollamaConfig = require('../services/ollamaConfig');
// Инициализируем один раз
const TIMEOUTS = ollamaConfig.getTimeouts();
// Проверка статуса подключения к Ollama
router.get('/status', requireAuth, async (req, res) => {
try {
const ollamaUrl = ollamaConfig.getBaseUrl();
// Проверяем API Ollama через HTTP запрос
try {
const response = await axios.get(`${ollamaUrl}/api/tags`, {
timeout: TIMEOUTS.ollamaTags // Централизованный таймаут
});
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 ollamaUrl = ollamaConfig.getBaseUrl();
const response = await axios.get(`${ollamaUrl}/api/tags`, {
timeout: TIMEOUTS.ollamaTags
});
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;