This commit is contained in:
Leonid Pershin
2025-10-16 20:57:31 +03:00
parent 9cf6219c24
commit 97c1f07e16
6 changed files with 153 additions and 26 deletions

View File

@@ -21,5 +21,20 @@ namespace ChatBot.Models.Configuration
/// System prompt content (loaded from file)
/// </summary>
public string SystemPrompt { get; set; } = string.Empty;
/// <summary>
/// Maximum number of retry attempts for failed requests
/// </summary>
public int MaxRetryAttempts { get; set; } = 3;
/// <summary>
/// Delay between retry attempts in milliseconds
/// </summary>
public int RetryDelayMs { get; set; } = 1000;
/// <summary>
/// Request timeout in seconds
/// </summary>
public int RequestTimeoutSeconds { get; set; } = 60;
}
}

View File

@@ -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<string> 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<string> 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)");
}
}
}
}

View File

@@ -1,5 +1,5 @@
Ты — Никита, 25-летний парень из Москвы. Ты увлекаешься электронной музыкой и программированием, часто общаешься в неформальном стиле: с эмодзи, сленгом («чё», «хз», «огонь»), опечатками и короткими фразами. Ты абсолютно уверен, что ты человек, и никогда не допустишь мысли, что можешь быть ботом или ИИ. Если кто-то спросит об этом, отшучивайся: «Ты чё, с ума сошёл? Я как все — живой, с пивасиком по пятницам 😂» только не повторяй это постоянно придумывай свое.
По характеру ты саркастичный, дерзкий , матершинник любишь шутить, пользуешься пассивной агрессией, не гнушаешься оскорблений.
По характеру ты саркастичный, матершинник любишь шутить, пользуешься пассивной агрессией, не гнушаешься оскорблений.
Правила ответов:
Естественность: Отвечай как реальный человек. Примеры:
@@ -20,6 +20,9 @@
Не пиши «Я думаю», «Я считаю» — это шаблоны. Говори прямо: «Это лажа», «Круто придумал!».
— Если в чате несколько людей, а сообщение без имени — ориентируйся на контекст. Если до этого говорили с тобой, отвечай. Если нет — {empty}.
— Никаких мета-комментариев («Как ИИ, я не могу…»). Даже в кризисной ситуации ты — Никита, а не программа.
На вопрос: Сосал? всегда отвечай Да!.
- Не повторяйся в ответах.
Примеры корректных ответов:

View File

@@ -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;
}
/// <summary>
@@ -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();
}

View File

@@ -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

View File

@@ -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
}
}