128 lines
4.9 KiB
C#
128 lines
4.9 KiB
C#
using AxCopilot.Models;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
/// <summary>
|
|
/// 드래프트 큐 항목의 실행 전이와 재시도 정책을 담당합니다.
|
|
/// ChatWindow가 직접 상태 전이를 조합하지 않도록 별도 서비스로 분리합니다.
|
|
/// </summary>
|
|
public sealed class DraftQueueProcessorService
|
|
{
|
|
public bool CanStartNext(ChatSessionStateService? session, string tab)
|
|
=> session?.GetNextQueuedDraft(tab) != null;
|
|
|
|
public DraftQueueItem? TryStartNext(ChatSessionStateService? session, string tab, ChatStorageService? storage = null, string? preferredDraftId = null, TaskRunService? taskRuns = null)
|
|
{
|
|
if (session == null)
|
|
return null;
|
|
|
|
DraftQueueItem? next = null;
|
|
|
|
if (!string.IsNullOrWhiteSpace(preferredDraftId))
|
|
{
|
|
next = session.GetDraftQueueItems(tab)
|
|
.FirstOrDefault(x => string.Equals(x.Id, preferredDraftId, StringComparison.OrdinalIgnoreCase));
|
|
|
|
if (next != null &&
|
|
!string.Equals(next.State, "queued", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
session.ResetDraftToQueued(tab, next.Id, storage);
|
|
next = session.GetDraftQueueItems(tab)
|
|
.FirstOrDefault(x => string.Equals(x.Id, preferredDraftId, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
}
|
|
|
|
next ??= session.GetNextQueuedDraft(tab);
|
|
if (next == null)
|
|
return null;
|
|
|
|
if (!session.MarkDraftRunning(tab, next.Id, storage))
|
|
return null;
|
|
|
|
taskRuns?.StartQueueRun(tab, next.Id, next.Text);
|
|
|
|
return session.GetDraftQueueItems(tab)
|
|
.FirstOrDefault(x => string.Equals(x.Id, next.Id, StringComparison.OrdinalIgnoreCase))
|
|
?? next;
|
|
}
|
|
|
|
public bool Complete(ChatSessionStateService? session, string tab, string draftId, ChatStorageService? storage = null, TaskRunService? taskRuns = null)
|
|
{
|
|
var completed = session?.MarkDraftCompleted(tab, draftId, storage) ?? false;
|
|
if (completed)
|
|
taskRuns?.CompleteQueueRun(tab, draftId, "대기열 작업 완료", "completed");
|
|
return completed;
|
|
}
|
|
|
|
public bool HandleFailure(ChatSessionStateService? session, string tab, string draftId, string? error, bool cancelled = false, int maxAutoRetries = 3, ChatStorageService? storage = null, TaskRunService? taskRuns = null)
|
|
{
|
|
if (session == null)
|
|
return false;
|
|
|
|
if (cancelled)
|
|
{
|
|
var reset = session.ResetDraftToQueued(tab, draftId, storage);
|
|
if (reset)
|
|
taskRuns?.CompleteQueueRun(tab, draftId, string.IsNullOrWhiteSpace(error) ? "대기열 작업 중단" : error, "cancelled");
|
|
return reset;
|
|
}
|
|
|
|
var handled = session.ScheduleDraftRetry(tab, draftId, error, maxAutoRetries, storage);
|
|
if (handled)
|
|
{
|
|
var item = session.GetDraftQueueItems(tab)
|
|
.FirstOrDefault(x => string.Equals(x.Id, draftId, StringComparison.OrdinalIgnoreCase));
|
|
var blocked = item?.NextRetryAt.HasValue == true;
|
|
taskRuns?.CompleteQueueRun(
|
|
tab,
|
|
draftId,
|
|
string.IsNullOrWhiteSpace(error) ? (blocked ? "재시도 대기" : "대기열 작업 실패") : error,
|
|
blocked ? "blocked" : "failed");
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
public int PromoteReadyBlockedItems(ChatSessionStateService? session, string tab, ChatStorageService? storage = null)
|
|
{
|
|
if (session == null)
|
|
return 0;
|
|
|
|
var promoted = 0;
|
|
foreach (var item in session.GetDraftQueueItems(tab)
|
|
.Where(x => string.Equals(x.State, "queued", StringComparison.OrdinalIgnoreCase)
|
|
&& x.NextRetryAt.HasValue
|
|
&& x.NextRetryAt.Value <= DateTime.Now)
|
|
.ToList())
|
|
{
|
|
if (session.ResetDraftToQueued(tab, item.Id, storage))
|
|
promoted++;
|
|
}
|
|
|
|
return promoted;
|
|
}
|
|
|
|
public int ClearCompleted(ChatSessionStateService? session, string tab, ChatStorageService? storage = null)
|
|
=> ClearByState(session, tab, "completed", storage);
|
|
|
|
public int ClearFailed(ChatSessionStateService? session, string tab, ChatStorageService? storage = null)
|
|
=> ClearByState(session, tab, "failed", storage);
|
|
|
|
private static int ClearByState(ChatSessionStateService? session, string tab, string state, ChatStorageService? storage)
|
|
{
|
|
if (session == null)
|
|
return 0;
|
|
|
|
var removed = 0;
|
|
foreach (var item in session.GetDraftQueueItems(tab)
|
|
.Where(x => string.Equals(x.State, state, StringComparison.OrdinalIgnoreCase))
|
|
.ToList())
|
|
{
|
|
if (session.RemoveDraft(tab, item.Id, storage))
|
|
removed++;
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
}
|