fixes
This commit is contained in:
@@ -7,6 +7,9 @@ namespace ChatBot.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChatSession
|
public class ChatSession
|
||||||
{
|
{
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
private readonly List<ChatMessage> _messageHistory = new List<ChatMessage>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique identifier for the chat session
|
/// Unique identifier for the chat session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -27,11 +30,6 @@ namespace ChatBot.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string ChatTitle { get; set; } = string.Empty;
|
public string ChatTitle { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// History of messages in this chat session
|
|
||||||
/// </summary>
|
|
||||||
public List<ChatMessage> MessageHistory { get; set; } = new List<ChatMessage>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AI model to use for this session
|
/// AI model to use for this session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -53,29 +51,32 @@ namespace ChatBot.Models
|
|||||||
public int MaxHistoryLength { get; set; } = 20;
|
public int MaxHistoryLength { get; set; } = 20;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a message to the history and manage history length
|
/// Add a message to the history and manage history length (thread-safe)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AddMessage(ChatMessage message)
|
public void AddMessage(ChatMessage message)
|
||||||
{
|
{
|
||||||
MessageHistory.Add(message);
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_messageHistory.Add(message);
|
||||||
LastUpdatedAt = DateTime.UtcNow;
|
LastUpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
// Trim history if it exceeds max length
|
// Trim history if it exceeds max length
|
||||||
if (MessageHistory.Count > MaxHistoryLength)
|
if (_messageHistory.Count > MaxHistoryLength)
|
||||||
{
|
{
|
||||||
// Keep system message if it exists, then keep the most recent messages
|
// Keep system message if it exists, then keep the most recent messages
|
||||||
var systemMessage = MessageHistory.FirstOrDefault(m => m.Role == "system");
|
var systemMessage = _messageHistory.FirstOrDefault(m => m.Role == "system");
|
||||||
var recentMessages = MessageHistory
|
var recentMessages = _messageHistory
|
||||||
.Where(m => m.Role != "system")
|
.Where(m => m.Role != "system")
|
||||||
.TakeLast(MaxHistoryLength - (systemMessage != null ? 1 : 0))
|
.TakeLast(MaxHistoryLength - (systemMessage != null ? 1 : 0))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
MessageHistory.Clear();
|
_messageHistory.Clear();
|
||||||
if (systemMessage != null)
|
if (systemMessage != null)
|
||||||
{
|
{
|
||||||
MessageHistory.Add(systemMessage);
|
_messageHistory.Add(systemMessage);
|
||||||
|
}
|
||||||
|
_messageHistory.AddRange(recentMessages);
|
||||||
}
|
}
|
||||||
MessageHistory.AddRange(recentMessages);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,20 +103,37 @@ namespace ChatBot.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get all messages
|
/// Get all messages (thread-safe)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ChatMessage> GetAllMessages()
|
public List<ChatMessage> GetAllMessages()
|
||||||
{
|
{
|
||||||
return new List<ChatMessage>(MessageHistory);
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return new List<ChatMessage>(_messageHistory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clear message history
|
/// Get the count of messages in history (thread-safe)
|
||||||
|
/// </summary>
|
||||||
|
public int GetMessageCount()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _messageHistory.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear message history (thread-safe)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ClearHistory()
|
public void ClearHistory()
|
||||||
{
|
{
|
||||||
MessageHistory.Clear();
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_messageHistory.Clear();
|
||||||
LastUpdatedAt = DateTime.UtcNow;
|
LastUpdatedAt = DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,10 +14,5 @@ namespace ChatBot.Models.Configuration
|
|||||||
/// Название модели по умолчанию
|
/// Название модели по умолчанию
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DefaultModel { get; set; } = "llama3";
|
public string DefaultModel { get; set; } = "llama3";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Максимальное количество повторных попыток при ошибках
|
|
||||||
/// </summary>
|
|
||||||
public int MaxRetries { get; set; } = 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ namespace ChatBot.Models.Configuration.Validators
|
|||||||
var errors = new List<string>();
|
var errors = new List<string>();
|
||||||
|
|
||||||
ValidateUrl(options, errors);
|
ValidateUrl(options, errors);
|
||||||
ValidateRetryAndTokenSettings(options, errors);
|
|
||||||
ValidateDefaultModel(options, errors);
|
ValidateDefaultModel(options, errors);
|
||||||
|
|
||||||
return errors.Count > 0
|
return errors.Count > 0
|
||||||
@@ -28,18 +27,6 @@ namespace ChatBot.Models.Configuration.Validators
|
|||||||
errors.Add($"Invalid Ollama URL format: {options.Url}");
|
errors.Add($"Invalid Ollama URL format: {options.Url}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateRetryAndTokenSettings(
|
|
||||||
OllamaSettings options,
|
|
||||||
List<string> 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<string> errors)
|
private static void ValidateDefaultModel(OllamaSettings options, List<string> errors)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(options.DefaultModel))
|
if (string.IsNullOrWhiteSpace(options.DefaultModel))
|
||||||
|
|||||||
@@ -68,8 +68,13 @@ try
|
|||||||
builder.Services.AddSingleton<BotInfoService>();
|
builder.Services.AddSingleton<BotInfoService>();
|
||||||
builder.Services.AddSingleton<ITelegramCommandProcessor, TelegramCommandProcessor>();
|
builder.Services.AddSingleton<ITelegramCommandProcessor, TelegramCommandProcessor>();
|
||||||
builder.Services.AddSingleton<ITelegramMessageHandler, TelegramMessageHandler>();
|
builder.Services.AddSingleton<ITelegramMessageHandler, TelegramMessageHandler>();
|
||||||
builder.Services.AddSingleton<ITelegramBotService, TelegramBotService>();
|
|
||||||
builder.Services.AddHostedService<TelegramBotService>();
|
// Регистрируем TelegramBotService как singleton и используем один экземпляр для интерфейса и HostedService
|
||||||
|
builder.Services.AddSingleton<TelegramBotService>();
|
||||||
|
builder.Services.AddSingleton<ITelegramBotService>(sp =>
|
||||||
|
sp.GetRequiredService<TelegramBotService>()
|
||||||
|
);
|
||||||
|
builder.Services.AddHostedService(sp => sp.GetRequiredService<TelegramBotService>());
|
||||||
|
|
||||||
// Регистрируем Health Checks
|
// Регистрируем Health Checks
|
||||||
builder
|
builder
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace ChatBot.Services.Telegram.Commands
|
|||||||
+ $"Тип чата: {session.ChatType}\n"
|
+ $"Тип чата: {session.ChatType}\n"
|
||||||
+ $"Название: {session.ChatTitle}\n"
|
+ $"Название: {session.ChatTitle}\n"
|
||||||
+ $"Модель: {session.Model}\n"
|
+ $"Модель: {session.Model}\n"
|
||||||
+ $"Сообщений в истории: {session.MessageHistory.Count}\n"
|
+ $"Сообщений в истории: {session.GetMessageCount()}\n"
|
||||||
+ $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}"
|
+ $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Telegram.Bot.Types;
|
|||||||
namespace ChatBot.Services.Telegram.Services
|
namespace ChatBot.Services.Telegram.Services
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Сервис для получения информации о боте
|
/// Сервис для получения информации о боте с улучшенным кэшированием
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BotInfoService
|
public class BotInfoService
|
||||||
{
|
{
|
||||||
@@ -12,6 +12,8 @@ namespace ChatBot.Services.Telegram.Services
|
|||||||
private readonly ILogger<BotInfoService> _logger;
|
private readonly ILogger<BotInfoService> _logger;
|
||||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||||
private User? _cachedBotInfo;
|
private User? _cachedBotInfo;
|
||||||
|
private DateTime? _cacheExpirationTime;
|
||||||
|
private readonly TimeSpan _cacheLifetime = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
public BotInfoService(ITelegramBotClient botClient, ILogger<BotInfoService> logger)
|
public BotInfoService(ITelegramBotClient botClient, ILogger<BotInfoService> logger)
|
||||||
{
|
{
|
||||||
@@ -20,32 +22,60 @@ namespace ChatBot.Services.Telegram.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получает информацию о боте (с кешированием)
|
/// Получает информацию о боте (с кэшированием и автоматической инвалидацией)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<User?> GetBotInfoAsync(CancellationToken cancellationToken = default)
|
public async Task<User?> GetBotInfoAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (_cachedBotInfo != null)
|
// Проверяем, есть ли валидный кэш
|
||||||
|
if (
|
||||||
|
_cachedBotInfo != null
|
||||||
|
&& _cacheExpirationTime.HasValue
|
||||||
|
&& DateTime.UtcNow < _cacheExpirationTime.Value
|
||||||
|
)
|
||||||
|
{
|
||||||
return _cachedBotInfo;
|
return _cachedBotInfo;
|
||||||
|
}
|
||||||
|
|
||||||
await _semaphore.WaitAsync(cancellationToken);
|
await _semaphore.WaitAsync(cancellationToken);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_cachedBotInfo != null)
|
// Double-check после получения блокировки
|
||||||
|
if (
|
||||||
|
_cachedBotInfo != null
|
||||||
|
&& _cacheExpirationTime.HasValue
|
||||||
|
&& DateTime.UtcNow < _cacheExpirationTime.Value
|
||||||
|
)
|
||||||
|
{
|
||||||
return _cachedBotInfo;
|
return _cachedBotInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Fetching bot info from Telegram API");
|
||||||
_cachedBotInfo = await _botClient.GetMe(cancellationToken: cancellationToken);
|
_cachedBotInfo = await _botClient.GetMe(cancellationToken: cancellationToken);
|
||||||
|
_cacheExpirationTime = DateTime.UtcNow.Add(_cacheLifetime);
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Bot info loaded: @{BotUsername} (ID: {BotId})",
|
"Bot info loaded and cached: @{BotUsername} (ID: {BotId}). Cache expires at {ExpirationTime}",
|
||||||
_cachedBotInfo.Username,
|
_cachedBotInfo.Username,
|
||||||
_cachedBotInfo.Id
|
_cachedBotInfo.Id,
|
||||||
|
_cacheExpirationTime
|
||||||
);
|
);
|
||||||
|
|
||||||
return _cachedBotInfo;
|
return _cachedBotInfo;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -53,5 +83,28 @@ namespace ChatBot.Services.Telegram.Services
|
|||||||
_semaphore.Release();
|
_semaphore.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Принудительно инвалидирует кэш информации о боте
|
||||||
|
/// </summary>
|
||||||
|
public void InvalidateCache()
|
||||||
|
{
|
||||||
|
lock (_semaphore)
|
||||||
|
{
|
||||||
|
_cachedBotInfo = null;
|
||||||
|
_cacheExpirationTime = null;
|
||||||
|
_logger.LogInformation("Bot info cache invalidated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверяет, есть ли валидная информация о боте в кэше
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCacheValid()
|
||||||
|
{
|
||||||
|
return _cachedBotInfo != null
|
||||||
|
&& _cacheExpirationTime.HasValue
|
||||||
|
&& DateTime.UtcNow < _cacheExpirationTime.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
},
|
},
|
||||||
"Ollama": {
|
"Ollama": {
|
||||||
"Url": "http://10.10.1.202:11434",
|
"Url": "http://10.10.1.202:11434",
|
||||||
"DefaultModel": "llama3",
|
"DefaultModel": "llama3chat"
|
||||||
"MaxRetries": 3
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user