diff --git a/TelegramSearchBot.LLMAgent/LLMAgentProgram.cs b/TelegramSearchBot.LLMAgent/LLMAgentProgram.cs index 3b9e7c8d..dfd95c7e 100644 --- a/TelegramSearchBot.LLMAgent/LLMAgentProgram.cs +++ b/TelegramSearchBot.LLMAgent/LLMAgentProgram.cs @@ -6,8 +6,10 @@ using TelegramSearchBot.Common; using TelegramSearchBot.Interface; using TelegramSearchBot.Interface.AI.LLM; +using TelegramSearchBot.Interface.Tools; using TelegramSearchBot.Model; using TelegramSearchBot.Service.AI.LLM; +using TelegramSearchBot.Service.Tools; namespace TelegramSearchBot.LLMAgent { public static class LLMAgentProgram { @@ -23,7 +25,10 @@ public static async Task RunAsync(string[] args) { using var services = BuildServices(port); var logger = services.GetRequiredService().CreateLogger("LLMAgent"); - McpToolHelper.EnsureInitialized(typeof(Service.AgentToolService).Assembly, services, logger); + McpToolHelper.EnsureInitialized( + typeof(Service.AgentToolService).Assembly, + typeof(FileToolService).Assembly, + services, logger); var loop = services.GetRequiredService(); using var shutdownCts = new CancellationTokenSource(); @@ -63,6 +68,8 @@ private static ServiceProvider BuildServices(int port) { services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); diff --git a/TelegramSearchBot.LLMAgent/Service/AgentLoopService.cs b/TelegramSearchBot.LLMAgent/Service/AgentLoopService.cs index 591c8dc8..fbabbd38 100644 --- a/TelegramSearchBot.LLMAgent/Service/AgentLoopService.cs +++ b/TelegramSearchBot.LLMAgent/Service/AgentLoopService.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using StackExchange.Redis; using TelegramSearchBot.Common; using TelegramSearchBot.Model.AI; @@ -42,7 +43,15 @@ public async Task RunAsync(long chatId, int port, CancellationToken cancellation break; } - var payload = await _garnetClient.BRPopAsync(LlmAgentRedisKeys.AgentTaskQueue, TimeSpan.FromSeconds(5)); + string? payload; + try { + payload = await _garnetClient.BRPopAsync(LlmAgentRedisKeys.AgentTaskQueue, TimeSpan.FromSeconds(2)); + } catch (RedisException ex) { + _logger.LogWarning(ex, "Redis error during BRPOP, retrying in 1s"); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + continue; + } + if (string.IsNullOrWhiteSpace(payload)) { continue; } @@ -186,9 +195,17 @@ await _garnetClient.PublishChunkAsync(new AgentStreamChunk { private async Task RunHeartbeatAsync(AgentSessionInfo session, CancellationToken cancellationToken) { using var timer = new PeriodicTimer(TimeSpan.FromSeconds(Math.Max(1, Env.AgentHeartbeatIntervalSeconds))); - while (!cancellationToken.IsCancellationRequested && await timer.WaitForNextTickAsync(cancellationToken)) { - session.LastHeartbeatUtc = DateTime.UtcNow; - await _rpcClient.SaveSessionAsync(session); + try { + while (!cancellationToken.IsCancellationRequested && await timer.WaitForNextTickAsync(cancellationToken)) { + try { + session.LastHeartbeatUtc = DateTime.UtcNow; + await _rpcClient.SaveSessionAsync(session); + } catch (RedisException ex) { + _logger.LogWarning(ex, "Redis error during heartbeat, will retry next tick"); + } + } + } catch (OperationCanceledException) { + // Normal shutdown – heartbeat stops } } diff --git a/TelegramSearchBot/Service/AI/LLM/AgentRegistryService.cs b/TelegramSearchBot/Service/AI/LLM/AgentRegistryService.cs index 3964485b..a62fdb93 100644 --- a/TelegramSearchBot/Service/AI/LLM/AgentRegistryService.cs +++ b/TelegramSearchBot/Service/AI/LLM/AgentRegistryService.cs @@ -183,6 +183,11 @@ public async Task RunMaintenanceOnceAsync(CancellationToken cancellationToken = } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + if (!Env.EnableLLMAgentProcess) { + _logger.LogDebug("LLM agent process mode disabled – AgentRegistryService will not start"); + return; + } + while (!stoppingToken.IsCancellationRequested) { try { await RunMaintenanceOnceAsync(stoppingToken); diff --git a/TelegramSearchBot/Service/AI/LLM/ChunkPollingService.cs b/TelegramSearchBot/Service/AI/LLM/ChunkPollingService.cs index ed437738..7759e6cc 100644 --- a/TelegramSearchBot/Service/AI/LLM/ChunkPollingService.cs +++ b/TelegramSearchBot/Service/AI/LLM/ChunkPollingService.cs @@ -52,9 +52,24 @@ public async Task RunPollCycleAsync(CancellationToken cancellationToken = defaul } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + if (!Env.EnableLLMAgentProcess) { + return; + } + while (!stoppingToken.IsCancellationRequested) { - await RunPollCycleAsync(stoppingToken); - await Task.Delay(Math.Max(50, Env.AgentChunkPollingIntervalMilliseconds), stoppingToken); + try { + await RunPollCycleAsync(stoppingToken); + } catch (OperationCanceledException) { + break; + } catch (RedisException) { + // Transient Redis failure – wait before retrying + } + + try { + await Task.Delay(Math.Max(50, Env.AgentChunkPollingIntervalMilliseconds), stoppingToken); + } catch (OperationCanceledException) { + break; + } } } diff --git a/TelegramSearchBot/Service/AI/LLM/TelegramTaskConsumer.cs b/TelegramSearchBot/Service/AI/LLM/TelegramTaskConsumer.cs index e4a07c92..afcbd03c 100644 --- a/TelegramSearchBot/Service/AI/LLM/TelegramTaskConsumer.cs +++ b/TelegramSearchBot/Service/AI/LLM/TelegramTaskConsumer.cs @@ -29,6 +29,11 @@ public TelegramTaskConsumer( } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + if (!Env.EnableLLMAgentProcess) { + _logger.LogDebug("LLM agent process mode disabled – TelegramTaskConsumer will not start"); + return; + } + while (!stoppingToken.IsCancellationRequested) { try { // Use a 2-second block time so BRPOP returns well within SE.Redis's 5 s async timeout.