fix errors

This commit is contained in:
Leonid Pershin
2025-10-17 02:36:47 +03:00
parent 0f28d988c0
commit 5ce7219703
3 changed files with 179 additions and 104 deletions

View File

@@ -9,40 +9,88 @@ namespace ChatBot.Migrations
/// <inheritdoc />
public partial class InitialCreate : Migration
{
private const string ChatSessionsTableName = "chat_sessions";
private const string ChatMessagesTableName = "chat_messages";
private const string ChatSessionsIdColumn = "id";
private const string ChatMessagesSessionIdColumn = "session_id";
private const string IntegerType = "integer";
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "chat_sessions",
name: ChatSessionsTableName,
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
session_id = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
id = table
.Column<int>(type: IntegerType, nullable: false)
.Annotation(
"Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
),
session_id = table.Column<string>(
type: "character varying(50)",
maxLength: 50,
nullable: false
),
chat_id = table.Column<long>(type: "bigint", nullable: false),
chat_type = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
chat_title = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
model = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
last_updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
max_history_length = table.Column<int>(type: "integer", nullable: false)
chat_type = table.Column<string>(
type: "character varying(20)",
maxLength: 20,
nullable: false
),
chat_title = table.Column<string>(
type: "character varying(200)",
maxLength: 200,
nullable: false
),
model = table.Column<string>(
type: "character varying(100)",
maxLength: 100,
nullable: false
),
created_at = table.Column<DateTime>(
type: "timestamp with time zone",
nullable: false
),
last_updated_at = table.Column<DateTime>(
type: "timestamp with time zone",
nullable: false
),
max_history_length = table.Column<int>(type: IntegerType, nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_chat_sessions", x => x.id);
});
}
);
migrationBuilder.CreateTable(
name: "chat_messages",
name: ChatMessagesTableName,
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
session_id = table.Column<int>(type: "integer", nullable: false),
content = table.Column<string>(type: "character varying(10000)", maxLength: 10000, nullable: false),
role = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
message_order = table.Column<int>(type: "integer", nullable: false)
id = table
.Column<int>(type: IntegerType, nullable: false)
.Annotation(
"Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
),
session_id = table.Column<int>(type: IntegerType, nullable: false),
content = table.Column<string>(
type: "character varying(10000)",
maxLength: 10000,
nullable: false
),
role = table.Column<string>(
type: "character varying(20)",
maxLength: 20,
nullable: false
),
created_at = table.Column<DateTime>(
type: "timestamp with time zone",
nullable: false
),
message_order = table.Column<int>(type: IntegerType, nullable: false),
},
constraints: table =>
{
@@ -50,46 +98,51 @@ namespace ChatBot.Migrations
table.ForeignKey(
name: "FK_chat_messages_chat_sessions_session_id",
column: x => x.session_id,
principalTable: "chat_sessions",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
principalTable: ChatSessionsTableName,
principalColumn: ChatSessionsIdColumn,
onDelete: ReferentialAction.Cascade
);
}
);
migrationBuilder.CreateIndex(
name: "IX_chat_messages_created_at",
table: "chat_messages",
column: "created_at");
table: ChatMessagesTableName,
column: "created_at"
);
migrationBuilder.CreateIndex(
name: "IX_chat_messages_session_id",
table: "chat_messages",
column: "session_id");
table: ChatMessagesTableName,
column: ChatMessagesSessionIdColumn
);
migrationBuilder.CreateIndex(
name: "IX_chat_messages_session_id_message_order",
table: "chat_messages",
columns: new[] { "session_id", "message_order" });
table: ChatMessagesTableName,
columns: new[] { ChatMessagesSessionIdColumn, "message_order" }
);
migrationBuilder.CreateIndex(
name: "IX_chat_sessions_chat_id",
table: "chat_sessions",
column: "chat_id");
table: ChatSessionsTableName,
column: "chat_id"
);
migrationBuilder.CreateIndex(
name: "IX_chat_sessions_session_id",
table: "chat_sessions",
table: ChatSessionsTableName,
column: "session_id",
unique: true);
unique: true
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "chat_messages");
migrationBuilder.DropTable(name: ChatMessagesTableName);
migrationBuilder.DropTable(
name: "chat_sessions");
migrationBuilder.DropTable(name: ChatSessionsTableName);
}
}
}

View File

@@ -288,94 +288,116 @@ namespace ChatBot.Services
CancellationToken cancellationToken
)
{
const int maxRetries = 2; // Fewer retries for summarization to avoid delays
const int maxRetries = 2;
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
var chatRequest = new OllamaSharp.Models.Chat.ChatRequest
{
Messages = messages
.Select(m => new OllamaSharp.Models.Chat.Message(m.Role, m.Content))
.ToList(),
Stream = false,
Options = new OllamaSharp.Models.RequestOptions
{
Temperature = 0.3f, // Lower temperature for more focused summaries
},
};
// Create timeout cancellation token for compression operations
using var timeoutCts = new CancellationTokenSource(
TimeSpan.FromSeconds(_aiSettings.CompressionTimeoutSeconds)
);
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
timeoutCts.Token
);
var response = new StringBuilder();
await foreach (
var chatResponse in _ollamaClient
.ChatAsync(chatRequest)
.WithCancellation(combinedCts.Token)
)
{
if (chatResponse?.Message?.Content != null)
{
response.Append(chatResponse.Message.Content);
}
}
var result = response.ToString().Trim();
return result.Length > _aiSettings.MaxSummarizedMessageLength
? result.Substring(0, _aiSettings.MaxSummarizedMessageLength) + "..."
: result;
return await TryGenerateSummaryAsync(messages, cancellationToken);
}
catch (OperationCanceledException ex)
when (ex.InnerException is TaskCanceledException)
{
_logger.LogWarning(
ex,
"Compression operation timed out after {TimeoutSeconds} seconds on attempt {Attempt}",
_aiSettings.CompressionTimeoutSeconds,
attempt
);
if (attempt == maxRetries)
{
if (HandleTimeoutAsync(attempt, maxRetries, ex))
return string.Empty;
}
}
catch (HttpRequestException ex) when (attempt < maxRetries)
{
var delay = 1000 * attempt; // Simple linear backoff for summarization
_logger.LogWarning(
ex,
"Failed to generate AI summary on attempt {Attempt}/{MaxAttempts}. Retrying in {DelayMs}ms...",
attempt,
maxRetries,
delay
);
await Task.Delay(delay, cancellationToken);
await HandleHttpExceptionAsync(attempt, maxRetries, ex, cancellationToken);
}
catch (Exception ex)
{
_logger.LogWarning(
ex,
"Failed to generate AI summary on attempt {Attempt}",
attempt
);
if (attempt == maxRetries)
{
if (HandleGenericExceptionAsync(attempt, maxRetries, ex))
return string.Empty;
}
}
}
return string.Empty;
}
private async Task<string> TryGenerateSummaryAsync(
List<ChatMessage> messages,
CancellationToken cancellationToken
)
{
var chatRequest = CreateSummaryRequest(messages);
using var timeoutCts = new CancellationTokenSource(
TimeSpan.FromSeconds(_aiSettings.CompressionTimeoutSeconds)
);
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
timeoutCts.Token
);
var response = new StringBuilder();
await foreach (
var chatResponse in _ollamaClient
.ChatAsync(chatRequest)
.WithCancellation(combinedCts.Token)
)
{
if (chatResponse?.Message?.Content != null)
{
response.Append(chatResponse.Message.Content);
}
}
var result = response.ToString().Trim();
return result.Length > _aiSettings.MaxSummarizedMessageLength
? result.Substring(0, _aiSettings.MaxSummarizedMessageLength) + "..."
: result;
}
private static OllamaSharp.Models.Chat.ChatRequest CreateSummaryRequest(
List<ChatMessage> messages
)
{
return new OllamaSharp.Models.Chat.ChatRequest
{
Messages = messages
.Select(m => new OllamaSharp.Models.Chat.Message(m.Role, m.Content))
.ToList(),
Stream = false,
Options = new OllamaSharp.Models.RequestOptions { Temperature = 0.3f },
};
}
private bool HandleTimeoutAsync(int attempt, int maxRetries, OperationCanceledException ex)
{
_logger.LogWarning(
ex,
"Compression operation timed out after {TimeoutSeconds} seconds on attempt {Attempt}",
_aiSettings.CompressionTimeoutSeconds,
attempt
);
return attempt == maxRetries;
}
private async Task HandleHttpExceptionAsync(
int attempt,
int maxRetries,
HttpRequestException ex,
CancellationToken cancellationToken
)
{
var delay = 1000 * attempt;
_logger.LogWarning(
ex,
"Failed to generate AI summary on attempt {Attempt}/{MaxAttempts}. Retrying in {DelayMs}ms...",
attempt,
maxRetries,
delay
);
await Task.Delay(delay, cancellationToken);
}
private bool HandleGenericExceptionAsync(int attempt, int maxRetries, Exception ex)
{
_logger.LogWarning(ex, "Failed to generate AI summary on attempt {Attempt}", attempt);
return attempt == maxRetries;
}
/// <summary>
/// Check if a single message should be kept (not too short, not too long)
/// </summary>

View File

@@ -53,7 +53,7 @@ namespace ChatBot.Services.Telegram.Commands
/// <summary>
/// Проверяет, есть ли аргументы у команды
/// </summary>
protected bool HasArguments(TelegramCommandContext context)
protected static bool HasArguments(TelegramCommandContext context)
{
return !string.IsNullOrWhiteSpace(context.Arguments);
}
@@ -61,7 +61,7 @@ namespace ChatBot.Services.Telegram.Commands
/// <summary>
/// Получает аргументы команды
/// </summary>
protected string GetArguments(TelegramCommandContext context)
protected static string GetArguments(TelegramCommandContext context)
{
return context.Arguments;
}