Initial commit to new repository
This commit is contained in:
127
src/AxCopilot/Services/DraftQueueProcessorService.cs
Normal file
127
src/AxCopilot/Services/DraftQueueProcessorService.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user