From e46013b70ba6426333c2b9ed52d0d7c78ceac259 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Thu, 16 Oct 2025 08:08:17 +0300 Subject: [PATCH] fixes --- ChatBot/Models/ChatSession.cs | 70 ++++++++++++------- .../Models/Configuration/OllamaSettings.cs | 5 -- .../Validators/OllamaSettingsValidator.cs | 13 ---- ChatBot/Program.cs | 9 ++- .../Telegram/Commands/SettingsCommand.cs | 2 +- .../Telegram/Services/BotInfoService.cs | 67 ++++++++++++++++-- ChatBot/appsettings.json | 3 +- 7 files changed, 113 insertions(+), 56 deletions(-) diff --git a/ChatBot/Models/ChatSession.cs b/ChatBot/Models/ChatSession.cs index d6d1910..8f9b5b0 100644 --- a/ChatBot/Models/ChatSession.cs +++ b/ChatBot/Models/ChatSession.cs @@ -7,6 +7,9 @@ namespace ChatBot.Models /// public class ChatSession { + private readonly object _lock = new object(); + private readonly List _messageHistory = new List(); + /// /// Unique identifier for the chat session /// @@ -27,11 +30,6 @@ namespace ChatBot.Models /// public string ChatTitle { get; set; } = string.Empty; - /// - /// History of messages in this chat session - /// - public List MessageHistory { get; set; } = new List(); - /// /// AI model to use for this session /// @@ -53,29 +51,32 @@ namespace ChatBot.Models public int MaxHistoryLength { get; set; } = 20; /// - /// Add a message to the history and manage history length + /// Add a message to the history and manage history length (thread-safe) /// public void AddMessage(ChatMessage message) { - MessageHistory.Add(message); - LastUpdatedAt = DateTime.UtcNow; - - // Trim history if it exceeds max length - if (MessageHistory.Count > MaxHistoryLength) + lock (_lock) { - // Keep system message if it exists, then keep the most recent messages - var systemMessage = MessageHistory.FirstOrDefault(m => m.Role == "system"); - var recentMessages = MessageHistory - .Where(m => m.Role != "system") - .TakeLast(MaxHistoryLength - (systemMessage != null ? 1 : 0)) - .ToList(); + _messageHistory.Add(message); + LastUpdatedAt = DateTime.UtcNow; - MessageHistory.Clear(); - if (systemMessage != null) + // Trim history if it exceeds max length + if (_messageHistory.Count > MaxHistoryLength) { - MessageHistory.Add(systemMessage); + // Keep system message if it exists, then keep the most recent messages + var systemMessage = _messageHistory.FirstOrDefault(m => m.Role == "system"); + var recentMessages = _messageHistory + .Where(m => m.Role != "system") + .TakeLast(MaxHistoryLength - (systemMessage != null ? 1 : 0)) + .ToList(); + + _messageHistory.Clear(); + if (systemMessage != null) + { + _messageHistory.Add(systemMessage); + } + _messageHistory.AddRange(recentMessages); } - MessageHistory.AddRange(recentMessages); } } @@ -102,20 +103,37 @@ namespace ChatBot.Models } /// - /// Get all messages + /// Get all messages (thread-safe) /// public List GetAllMessages() { - return new List(MessageHistory); + lock (_lock) + { + return new List(_messageHistory); + } } /// - /// Clear message history + /// Get the count of messages in history (thread-safe) + /// + public int GetMessageCount() + { + lock (_lock) + { + return _messageHistory.Count; + } + } + + /// + /// Clear message history (thread-safe) /// public void ClearHistory() { - MessageHistory.Clear(); - LastUpdatedAt = DateTime.UtcNow; + lock (_lock) + { + _messageHistory.Clear(); + LastUpdatedAt = DateTime.UtcNow; + } } } } diff --git a/ChatBot/Models/Configuration/OllamaSettings.cs b/ChatBot/Models/Configuration/OllamaSettings.cs index aa3c50f..618fa4e 100644 --- a/ChatBot/Models/Configuration/OllamaSettings.cs +++ b/ChatBot/Models/Configuration/OllamaSettings.cs @@ -14,10 +14,5 @@ namespace ChatBot.Models.Configuration /// Название модели по умолчанию /// public string DefaultModel { get; set; } = "llama3"; - - /// - /// Максимальное количество повторных попыток при ошибках - /// - public int MaxRetries { get; set; } = 3; } } diff --git a/ChatBot/Models/Configuration/Validators/OllamaSettingsValidator.cs b/ChatBot/Models/Configuration/Validators/OllamaSettingsValidator.cs index 419deb0..6846431 100644 --- a/ChatBot/Models/Configuration/Validators/OllamaSettingsValidator.cs +++ b/ChatBot/Models/Configuration/Validators/OllamaSettingsValidator.cs @@ -12,7 +12,6 @@ namespace ChatBot.Models.Configuration.Validators var errors = new List(); ValidateUrl(options, errors); - ValidateRetryAndTokenSettings(options, errors); ValidateDefaultModel(options, errors); return errors.Count > 0 @@ -28,18 +27,6 @@ namespace ChatBot.Models.Configuration.Validators errors.Add($"Invalid Ollama URL format: {options.Url}"); } - private static void ValidateRetryAndTokenSettings( - OllamaSettings options, - List errors - ) - { - if (options.MaxRetries < 1) - errors.Add($"MaxRetries must be at least 1, got: {options.MaxRetries}"); - - if (options.MaxRetries > 10) - errors.Add($"MaxRetries should not exceed 10, got: {options.MaxRetries}"); - } - private static void ValidateDefaultModel(OllamaSettings options, List errors) { if (string.IsNullOrWhiteSpace(options.DefaultModel)) diff --git a/ChatBot/Program.cs b/ChatBot/Program.cs index adec8ff..c88f898 100644 --- a/ChatBot/Program.cs +++ b/ChatBot/Program.cs @@ -68,8 +68,13 @@ try builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddHostedService(); + + // Регистрируем TelegramBotService как singleton и используем один экземпляр для интерфейса и HostedService + builder.Services.AddSingleton(); + builder.Services.AddSingleton(sp => + sp.GetRequiredService() + ); + builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Регистрируем Health Checks builder diff --git a/ChatBot/Services/Telegram/Commands/SettingsCommand.cs b/ChatBot/Services/Telegram/Commands/SettingsCommand.cs index d3408d9..ef3873c 100644 --- a/ChatBot/Services/Telegram/Commands/SettingsCommand.cs +++ b/ChatBot/Services/Telegram/Commands/SettingsCommand.cs @@ -30,7 +30,7 @@ namespace ChatBot.Services.Telegram.Commands + $"Тип чата: {session.ChatType}\n" + $"Название: {session.ChatTitle}\n" + $"Модель: {session.Model}\n" - + $"Сообщений в истории: {session.MessageHistory.Count}\n" + + $"Сообщений в истории: {session.GetMessageCount()}\n" + $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}" ); } diff --git a/ChatBot/Services/Telegram/Services/BotInfoService.cs b/ChatBot/Services/Telegram/Services/BotInfoService.cs index 9a086fd..38ecb5a 100644 --- a/ChatBot/Services/Telegram/Services/BotInfoService.cs +++ b/ChatBot/Services/Telegram/Services/BotInfoService.cs @@ -4,7 +4,7 @@ using Telegram.Bot.Types; namespace ChatBot.Services.Telegram.Services { /// - /// Сервис для получения информации о боте + /// Сервис для получения информации о боте с улучшенным кэшированием /// public class BotInfoService { @@ -12,6 +12,8 @@ namespace ChatBot.Services.Telegram.Services private readonly ILogger _logger; private readonly SemaphoreSlim _semaphore = new(1, 1); private User? _cachedBotInfo; + private DateTime? _cacheExpirationTime; + private readonly TimeSpan _cacheLifetime = TimeSpan.FromHours(1); public BotInfoService(ITelegramBotClient botClient, ILogger logger) { @@ -20,32 +22,60 @@ namespace ChatBot.Services.Telegram.Services } /// - /// Получает информацию о боте (с кешированием) + /// Получает информацию о боте (с кэшированием и автоматической инвалидацией) /// public async Task GetBotInfoAsync(CancellationToken cancellationToken = default) { - if (_cachedBotInfo != null) + // Проверяем, есть ли валидный кэш + if ( + _cachedBotInfo != null + && _cacheExpirationTime.HasValue + && DateTime.UtcNow < _cacheExpirationTime.Value + ) + { return _cachedBotInfo; + } await _semaphore.WaitAsync(cancellationToken); try { - if (_cachedBotInfo != null) + // Double-check после получения блокировки + if ( + _cachedBotInfo != null + && _cacheExpirationTime.HasValue + && DateTime.UtcNow < _cacheExpirationTime.Value + ) + { return _cachedBotInfo; + } + _logger.LogDebug("Fetching bot info from Telegram API"); _cachedBotInfo = await _botClient.GetMe(cancellationToken: cancellationToken); + _cacheExpirationTime = DateTime.UtcNow.Add(_cacheLifetime); _logger.LogInformation( - "Bot info loaded: @{BotUsername} (ID: {BotId})", + "Bot info loaded and cached: @{BotUsername} (ID: {BotId}). Cache expires at {ExpirationTime}", _cachedBotInfo.Username, - _cachedBotInfo.Id + _cachedBotInfo.Id, + _cacheExpirationTime ); return _cachedBotInfo; } catch (Exception ex) { - _logger.LogError(ex, "Failed to get bot info"); + _logger.LogError(ex, "Failed to get bot info from Telegram API"); + + // Если есть устаревший кэш, используем его + if (_cachedBotInfo != null) + { + _logger.LogWarning( + "Using stale cached bot info due to API error: @{BotUsername}", + _cachedBotInfo.Username + ); + return _cachedBotInfo; + } + return null; } finally @@ -53,5 +83,28 @@ namespace ChatBot.Services.Telegram.Services _semaphore.Release(); } } + + /// + /// Принудительно инвалидирует кэш информации о боте + /// + public void InvalidateCache() + { + lock (_semaphore) + { + _cachedBotInfo = null; + _cacheExpirationTime = null; + _logger.LogInformation("Bot info cache invalidated"); + } + } + + /// + /// Проверяет, есть ли валидная информация о боте в кэше + /// + public bool IsCacheValid() + { + return _cachedBotInfo != null + && _cacheExpirationTime.HasValue + && DateTime.UtcNow < _cacheExpirationTime.Value; + } } } diff --git a/ChatBot/appsettings.json b/ChatBot/appsettings.json index 9b6d9d5..96f0bd8 100644 --- a/ChatBot/appsettings.json +++ b/ChatBot/appsettings.json @@ -33,7 +33,6 @@ }, "Ollama": { "Url": "http://10.10.1.202:11434", - "DefaultModel": "llama3", - "MaxRetries": 3 + "DefaultModel": "llama3chat" } }