/** * 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 */ /** * Конфигурационный сервис для Ollama и AI инфраструктуры * Централизует все настройки, URL и таймауты для: * - Ollama API * - Vector Search * - AI Cache * - AI Queue * * ВАЖНО: Настройки берутся из таблицы ai_providers_settings (через aiProviderSettingsService) */ const logger = require('../utils/logger'); // Кэш для настроек из БД let settingsCache = null; /** * Загружает настройки Ollama из базы данных * @returns {Promise} Настройки Ollama провайдера */ async function loadSettingsFromDb() { try { const aiProviderSettingsService = require('./aiProviderSettingsService'); const settings = await aiProviderSettingsService.getProviderSettings('ollama'); if (settings) { settingsCache = settings; logger.info(`[ollamaConfig] Loaded settings from DB: model=${settings.selected_model}, base_url=${settings.base_url}`); } return settings; } catch (error) { logger.error('[ollamaConfig] Ошибка загрузки настроек Ollama из БД:', error.message); return null; } } /** * Внутренняя функция: определяет base URL из доступных источников * Приоритет: кэш из БД > переменная окружения > Docker дефолт * @returns {string} Базовый URL Ollama */ function _getBaseUrlFromSources() { // Приоритет 1: кэш из БД if (settingsCache && settingsCache.base_url) { return settingsCache.base_url; } // Приоритет 2: переменная окружения if (process.env.OLLAMA_BASE_URL) { return process.env.OLLAMA_BASE_URL; } // Приоритет 3: Docker дефолт return 'http://ollama:11434'; } /** * Получает базовый URL для Ollama (синхронная версия) * @returns {string} Базовый URL Ollama */ function getBaseUrl() { return _getBaseUrlFromSources(); } /** * Получает базовый URL для Ollama (асинхронная версия) * @returns {Promise} Базовый URL Ollama */ async function getBaseUrlAsync() { try { if (!settingsCache) { await loadSettingsFromDb(); } } catch (error) { logger.warn('[ollamaConfig] Failed to load base_url from DB, using default'); } return _getBaseUrlFromSources(); } /** * Получает URL для конкретного API endpoint Ollama * @param {string} endpoint - Endpoint API (например: 'tags', 'generate') * @returns {string} Полный URL для API endpoint */ function getApiUrl(endpoint) { const baseUrl = getBaseUrl(); return `${baseUrl}/api/${endpoint}`; } /** * Получает модель по умолчанию для Ollama (синхронная версия) * @returns {string} Название модели */ function getDefaultModel() { // Приоритет: кэш из БД > дефолт if (settingsCache && settingsCache.selected_model) { return settingsCache.selected_model; } // Дефолтное значение если БД недоступна return 'qwen2.5:7b'; } /** * Получает модель асинхронно из БД * @returns {Promise} Название модели из БД */ async function getDefaultModelAsync() { try { if (!settingsCache) { await loadSettingsFromDb(); } if (settingsCache && settingsCache.selected_model) { logger.info(`[ollamaConfig] Using model from DB: ${settingsCache.selected_model}`); return settingsCache.selected_model; } } catch (error) { logger.warn('[ollamaConfig] Failed to load model from DB, using default'); } return 'qwen2.5:7b'; } /** * Получает embedding модель асинхронно из БД * @returns {Promise} Название embedding модели из БД */ async function getEmbeddingModel() { try { if (!settingsCache) { await loadSettingsFromDb(); } if (settingsCache && settingsCache.embedding_model) { logger.info(`[ollamaConfig] Using embedding model from DB: ${settingsCache.embedding_model}`); return settingsCache.embedding_model; } } catch (error) { logger.warn('[ollamaConfig] Failed to load embedding model from DB, using default'); } return 'mxbai-embed-large:latest'; } /** * Централизованные таймауты для Ollama и AI сервисов * @returns {Object} Объект с различными таймаутами */ function getTimeouts() { return { // Ollama API - таймауты запросов ollamaChat: 180000, // 180 сек (3 мин) - генерация ответов LLM (увеличено для сложных запросов) ollamaEmbedding: 90000, // 90 сек (1.5 мин) - генерация embeddings (увеличено) ollamaHealth: 5000, // 5 сек - health check ollamaTags: 10000, // 10 сек - список моделей // Vector Search - таймауты запросов vectorSearch: 90000, // 90 сек - поиск по векторам (увеличено для больших баз) vectorUpsert: 90000, // 90 сек - индексация данных (увеличено) vectorHealth: 5000, // 5 сек - health check // AI Cache - TTL (Time To Live) для кэширования cacheLLM: 24 * 60 * 60 * 1000, // 24 часа - LLM ответы cacheRAG: 5 * 60 * 1000, // 5 минут - RAG результаты cacheMax: 1000, // Максимум записей в кэше // AI Queue - параметры очереди queueTimeout: 180000, // 180 сек - таймаут задачи в очереди (увеличено) queueMaxSize: 100, // Максимум задач в очереди queueInterval: 100, // 100 мс - интервал проверки очереди // Default для совместимости default: 180000 // 180 сек (увеличено с 120) }; } /** * Получает timeout для запросов к Ollama (обратная совместимость) * @returns {number} Timeout в миллисекундах */ function getTimeout() { return getTimeouts().ollamaChat; // 120 секунд (2 минуты) - для генерации длинных ответов } /** * Получает все конфигурационные параметры Ollama (синхронная версия) * @returns {Object} Объект с конфигурацией */ function getConfig() { return { baseUrl: getBaseUrl(), defaultModel: getDefaultModel(), timeout: getTimeout(), apiUrl: { tags: getApiUrl('tags'), generate: getApiUrl('generate'), chat: getApiUrl('chat'), models: getApiUrl('models'), show: getApiUrl('show'), pull: getApiUrl('pull'), push: getApiUrl('push') } }; } /** * Получает все конфигурационные параметры Ollama (асинхронная версия) * @returns {Promise} Объект с конфигурацией */ async function getConfigAsync() { const baseUrl = await getBaseUrlAsync(); const defaultModel = await getDefaultModelAsync(); const embeddingModel = await getEmbeddingModel(); return { baseUrl, defaultModel, embeddingModel, timeout: getTimeout(), apiUrl: { tags: `${baseUrl}/api/tags`, generate: `${baseUrl}/api/generate`, chat: `${baseUrl}/api/chat`, models: `${baseUrl}/api/models`, show: `${baseUrl}/api/show`, pull: `${baseUrl}/api/pull`, push: `${baseUrl}/api/push` } }; } /** * Очищает кэш настроек (для перезагрузки) */ function clearCache() { settingsCache = null; logger.info('[ollamaConfig] Settings cache cleared'); } /** * Проверяет доступность Ollama сервиса * @returns {Promise} Статус здоровья сервиса */ async function checkHealth() { try { const baseUrl = getBaseUrl(); const response = await fetch(`${baseUrl}/api/tags`); if (!response.ok) { return { status: 'error', error: `Ollama вернул код ${response.status}`, baseUrl }; } const data = await response.json(); return { status: 'ok', baseUrl, model: getDefaultModel(), availableModels: data.models?.length || 0 }; } catch (error) { return { status: 'error', error: error.message, baseUrl: getBaseUrl() }; } } module.exports = { getBaseUrl, getBaseUrlAsync, getApiUrl, getDefaultModel, getDefaultModelAsync, getEmbeddingModel, getTimeout, // Обратная совместимость (возвращает ollamaChat timeout) getTimeouts, // ✨ НОВОЕ: Централизованные таймауты для всех сервисов getConfig, getConfigAsync, loadSettingsFromDb, clearCache, checkHealth };