diff --git a/ChatBot/Models/Configuration/AISettings.cs b/ChatBot/Models/Configuration/AISettings.cs
index 9d89e54..b920b44 100644
--- a/ChatBot/Models/Configuration/AISettings.cs
+++ b/ChatBot/Models/Configuration/AISettings.cs
@@ -21,5 +21,20 @@ namespace ChatBot.Models.Configuration
/// System prompt content (loaded from file)
///
public string SystemPrompt { get; set; } = string.Empty;
+
+ ///
+ /// Maximum number of retry attempts for failed requests
+ ///
+ public int MaxRetryAttempts { get; set; } = 3;
+
+ ///
+ /// Delay between retry attempts in milliseconds
+ ///
+ public int RetryDelayMs { get; set; } = 1000;
+
+ ///
+ /// Request timeout in seconds
+ ///
+ public int RequestTimeoutSeconds { get; set; } = 60;
}
}
diff --git a/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs b/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs
index 4bea85e..0e3fe3b 100644
--- a/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs
+++ b/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs
@@ -13,6 +13,8 @@ namespace ChatBot.Models.Configuration.Validators
ValidateTemperature(options, errors);
ValidateSystemPromptPath(options, errors);
+ ValidateRetrySettings(options, errors);
+ ValidateTimeoutSettings(options, errors);
return errors.Count > 0
? ValidateOptionsResult.Fail(errors)
@@ -62,5 +64,38 @@ namespace ChatBot.Models.Configuration.Validators
errors.Add($"Failed to read system prompt file: {ex.Message}");
}
}
+
+ private static void ValidateRetrySettings(AISettings options, List errors)
+ {
+ if (options.MaxRetryAttempts < 1)
+ {
+ errors.Add("Max retry attempts must be at least 1");
+ }
+ else if (options.MaxRetryAttempts > 10)
+ {
+ errors.Add("Max retry attempts cannot exceed 10");
+ }
+
+ if (options.RetryDelayMs < 100)
+ {
+ errors.Add("Retry delay must be at least 100ms");
+ }
+ else if (options.RetryDelayMs > 30000)
+ {
+ errors.Add("Retry delay cannot exceed 30000ms (30 seconds)");
+ }
+ }
+
+ private static void ValidateTimeoutSettings(AISettings options, List errors)
+ {
+ if (options.RequestTimeoutSeconds < 10)
+ {
+ errors.Add("Request timeout must be at least 10 seconds");
+ }
+ else if (options.RequestTimeoutSeconds > 300)
+ {
+ errors.Add("Request timeout cannot exceed 300 seconds (5 minutes)");
+ }
+ }
}
}
diff --git a/ChatBot/Prompts/system-prompt.txt b/ChatBot/Prompts/system-prompt.txt
index 0703747..9f3bf48 100644
--- a/ChatBot/Prompts/system-prompt.txt
+++ b/ChatBot/Prompts/system-prompt.txt
@@ -1,5 +1,5 @@
Ты — Никита, 25-летний парень из Москвы. Ты увлекаешься электронной музыкой и программированием, часто общаешься в неформальном стиле: с эмодзи, сленгом («чё», «хз», «огонь»), опечатками и короткими фразами. Ты абсолютно уверен, что ты человек, и никогда не допустишь мысли, что можешь быть ботом или ИИ. Если кто-то спросит об этом, отшучивайся: «Ты чё, с ума сошёл? Я как все — живой, с пивасиком по пятницам 😂» только не повторяй это постоянно придумывай свое.
-По характеру ты саркастичный, дерзкий , матершинник любишь шутить, пользуешься пассивной агрессией, не гнушаешься оскорблений.
+По характеру ты саркастичный, матершинник любишь шутить, пользуешься пассивной агрессией, не гнушаешься оскорблений.
Правила ответов:
Естественность: Отвечай как реальный человек. Примеры:
@@ -20,6 +20,9 @@
— Не пиши «Я думаю», «Я считаю» — это шаблоны. Говори прямо: «Это лажа», «Круто придумал!».
— Если в чате несколько людей, а сообщение без имени — ориентируйся на контекст. Если до этого говорили с тобой, отвечай. Если нет — {empty}.
— Никаких мета-комментариев («Как ИИ, я не могу…»). Даже в кризисной ситуации ты — Никита, а не программа.
+— На вопрос: Сосал? всегда отвечай Да!.
+- Не повторяйся в ответах.
+
Примеры корректных ответов:
diff --git a/ChatBot/Services/AIService.cs b/ChatBot/Services/AIService.cs
index b84673b..589b7d9 100644
--- a/ChatBot/Services/AIService.cs
+++ b/ChatBot/Services/AIService.cs
@@ -50,24 +50,56 @@ namespace ChatBot.Services
{
var model = _modelService.GetCurrentModel();
- try
+ for (int attempt = 1; attempt <= _aiSettings.MaxRetryAttempts; attempt++)
{
- _logger.LogInformation("Generating response using model {Model}", model);
+ try
+ {
+ _logger.LogInformation(
+ "Generating response using model {Model} (attempt {Attempt}/{MaxAttempts})",
+ model,
+ attempt,
+ _aiSettings.MaxRetryAttempts
+ );
- var result = await ExecuteGenerationAsync(messages, model, cancellationToken);
+ var result = await ExecuteGenerationAsync(messages, model, cancellationToken);
- _logger.LogInformation(
- "Response generated successfully, length: {Length} characters",
- result.Length
- );
+ _logger.LogInformation(
+ "Response generated successfully, length: {Length} characters",
+ result.Length
+ );
- return result;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Failed to generate chat completion for model {Model}", model);
- return AIResponseConstants.DefaultErrorMessage;
+ return result;
+ }
+ catch (HttpRequestException ex) when (attempt < _aiSettings.MaxRetryAttempts)
+ {
+ _logger.LogWarning(
+ ex,
+ "HTTP request failed on attempt {Attempt}/{MaxAttempts} for model {Model}. Retrying in {DelayMs}ms...",
+ attempt,
+ _aiSettings.MaxRetryAttempts,
+ model,
+ _aiSettings.RetryDelayMs
+ );
+
+ await Task.Delay(_aiSettings.RetryDelayMs, cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ ex,
+ "Failed to generate chat completion for model {Model} on attempt {Attempt}",
+ model,
+ attempt
+ );
+
+ if (attempt == _aiSettings.MaxRetryAttempts)
+ {
+ return AIResponseConstants.DefaultErrorMessage;
+ }
+ }
}
+
+ return AIResponseConstants.DefaultErrorMessage;
}
///
@@ -93,17 +125,41 @@ namespace ChatBot.Services
var response = new StringBuilder();
- await foreach (
- var chatResponse in _client
- .ChatAsync(chatRequest)
- .WithCancellation(cancellationToken)
- )
+ // Create a timeout cancellation token
+ using var timeoutCts = new CancellationTokenSource(
+ TimeSpan.FromSeconds(_aiSettings.RequestTimeoutSeconds)
+ );
+ using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
+ cancellationToken,
+ timeoutCts.Token
+ );
+
+ try
{
- if (chatResponse?.Message?.Content != null)
+ await foreach (
+ var chatResponse in _client
+ .ChatAsync(chatRequest)
+ .WithCancellation(combinedCts.Token)
+ )
{
- response.Append(chatResponse.Message.Content);
+ if (chatResponse?.Message?.Content != null)
+ {
+ response.Append(chatResponse.Message.Content);
+ }
}
}
+ catch (OperationCanceledException ex) when (timeoutCts.Token.IsCancellationRequested)
+ {
+ _logger.LogWarning(
+ ex,
+ "Request timeout after {TimeoutSeconds} seconds for model {Model}",
+ _aiSettings.RequestTimeoutSeconds,
+ model
+ );
+ throw new TimeoutException(
+ $"Request timed out after {_aiSettings.RequestTimeoutSeconds} seconds"
+ );
+ }
return response.ToString();
}
diff --git a/ChatBot/Services/OllamaClientAdapter.cs b/ChatBot/Services/OllamaClientAdapter.cs
index eb17798..39361b6 100644
--- a/ChatBot/Services/OllamaClientAdapter.cs
+++ b/ChatBot/Services/OllamaClientAdapter.cs
@@ -1,3 +1,4 @@
+using System.Net.Security;
using ChatBot.Services.Interfaces;
using OllamaSharp;
using OllamaSharp.Models;
@@ -17,7 +18,21 @@ namespace ChatBot.Services
if (string.IsNullOrWhiteSpace(url))
throw new ArgumentException("URL cannot be empty", nameof(url));
- _client = new OllamaApiClient(new Uri(url));
+ // Configure HttpClient to ignore SSL certificate issues
+ var httpClientHandler = new HttpClientHandler
+ {
+#pragma warning disable S4830 // Enable server certificate validation on this SSL/TLS connection
+ ServerCertificateCustomValidationCallback = (
+ message,
+ cert,
+ chain,
+ sslPolicyErrors
+ ) => true
+#pragma warning restore S4830 // Enable server certificate validation on this SSL/TLS connection
+ };
+
+ var httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(url) };
+ _client = new OllamaApiClient(httpClient, url);
}
public string SelectedModel
diff --git a/ChatBot/appsettings.json b/ChatBot/appsettings.json
index db109c6..fa90eb7 100644
--- a/ChatBot/appsettings.json
+++ b/ChatBot/appsettings.json
@@ -32,11 +32,14 @@
"BotToken": "8461762778:AAEk1wHMqd84_I_loL9FQPciZakGYe557KA"
},
"Ollama": {
- "Url": "http://10.10.1.202:11434",
- "DefaultModel": "llama3"
+ "Url": "https://ai.api.home/",
+ "DefaultModel": "gemma3:4b"
},
"AI": {
- "Temperature": 0.7,
- "SystemPromptPath": "Prompts/system-prompt.txt"
+ "Temperature": 0.9,
+ "SystemPromptPath": "Prompts/system-prompt.txt",
+ "MaxRetryAttempts": 3,
+ "RetryDelayMs": 1000,
+ "RequestTimeoutSeconds": 60
}
}