AX Agent 실행 마감 helper 통합과 계획 노출 최소화
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- send와 regenerate가 같은 실행/예외/취소/최종 커밋/후처리 helper를 타도록 ExecutePreparedTurnAsync 추가 - 계획 이벤트는 기본적으로 얇은 요약 pill만 노출하고 debug에서만 큰 카드가 보이도록 조정 - README와 DEVELOPMENT 문서에 2026-04-05 18:08 (KST) 기준 이력 반영 - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0 / 오류 0)
This commit is contained in:
@@ -8372,30 +8372,17 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
var response = await _chatEngine.ExecutePreparedAsync(
|
||||
var executionOutcome = await ExecutePreparedTurnAsync(
|
||||
conv,
|
||||
runTab,
|
||||
originTab,
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, originTab, conv, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
StopAiIconPulse();
|
||||
_cachedStreamContent = response;
|
||||
|
||||
draftSucceeded = true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true);
|
||||
assistantContent = finalized.Content;
|
||||
draftCancelled = finalized.Cancelled;
|
||||
draftFailure = finalized.FailureReason;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, ex);
|
||||
assistantContent = finalized.Content;
|
||||
ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600);
|
||||
draftFailure = finalized.FailureReason;
|
||||
"응답 생성 중...");
|
||||
conv = executionOutcome.Conversation;
|
||||
assistantContent = executionOutcome.AssistantContent;
|
||||
draftSucceeded = executionOutcome.DraftSucceeded;
|
||||
draftCancelled = executionOutcome.DraftCancelled;
|
||||
draftFailure = executionOutcome.DraftFailure;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -8405,18 +8392,7 @@ public partial class ChatWindow : Window
|
||||
_llm.ClearRouteOverride();
|
||||
UpdateModelLabel();
|
||||
}
|
||||
ResetStreamingUiState();
|
||||
}
|
||||
|
||||
lock (_convLock)
|
||||
{
|
||||
var session = ChatSession;
|
||||
assistantContent = _chatEngine.FinalizeAssistantTurn(session, conv, runTab, assistantContent, _storage);
|
||||
_currentConversation = session?.CurrentConversation ?? conv;
|
||||
conv = _currentConversation!;
|
||||
}
|
||||
|
||||
FinalizeConversationTurn(originTab, conv);
|
||||
FinalizeQueuedDraft(originTab, queuedDraftId, draftSucceeded, draftCancelled, draftFailure);
|
||||
}
|
||||
|
||||
@@ -8553,6 +8529,87 @@ public partial class ChatWindow : Window
|
||||
_ = Dispatcher.BeginInvoke(new Action(() => StartNextQueuedDraftIfAny()), DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private sealed record PreparedTurnOutcome(
|
||||
ChatConversation Conversation,
|
||||
string AssistantContent,
|
||||
bool DraftSucceeded,
|
||||
bool DraftCancelled,
|
||||
string? DraftFailure);
|
||||
|
||||
private async Task<PreparedTurnOutcome> ExecutePreparedTurnAsync(
|
||||
ChatConversation conversation,
|
||||
string runTab,
|
||||
string rememberTab,
|
||||
AxAgentExecutionEngine.PreparedExecution preparedExecution,
|
||||
string busyStatus)
|
||||
{
|
||||
_isStreaming = true;
|
||||
_streamRunTab = runTab;
|
||||
BtnSend.IsEnabled = false;
|
||||
BtnSend.Visibility = Visibility.Collapsed;
|
||||
BtnStop.Visibility = Visibility.Visible;
|
||||
if (runTab == "Cowork" || runTab == "Code")
|
||||
BtnPause.Visibility = Visibility.Visible;
|
||||
_streamCts = new CancellationTokenSource();
|
||||
ForceScrollToEnd();
|
||||
|
||||
var assistantContent = string.Empty;
|
||||
var draftSucceeded = false;
|
||||
var draftCancelled = false;
|
||||
string? draftFailure = null;
|
||||
|
||||
_activeStreamText = null;
|
||||
_cachedStreamContent = "";
|
||||
_displayedLength = 0;
|
||||
_cursorVisible = true;
|
||||
_aiIconPulseStopped = false;
|
||||
_streamStartTime = DateTime.UtcNow;
|
||||
_elapsedTimer.Start();
|
||||
SetStatus(busyStatus, spinning: true);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _chatEngine.ExecutePreparedAsync(
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, rememberTab, conversation, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
StopAiIconPulse();
|
||||
_cachedStreamContent = response;
|
||||
draftSucceeded = true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true);
|
||||
assistantContent = finalized.Content;
|
||||
draftCancelled = finalized.Cancelled;
|
||||
draftFailure = finalized.FailureReason;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, ex);
|
||||
assistantContent = finalized.Content;
|
||||
draftFailure = finalized.FailureReason;
|
||||
ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetStreamingUiState();
|
||||
}
|
||||
|
||||
lock (_convLock)
|
||||
{
|
||||
var session = ChatSession;
|
||||
assistantContent = _chatEngine.FinalizeAssistantTurn(session, conversation, runTab, assistantContent, _storage);
|
||||
_currentConversation = session?.CurrentConversation ?? conversation;
|
||||
conversation = _currentConversation!;
|
||||
}
|
||||
|
||||
FinalizeConversationTurn(rememberTab, conversation);
|
||||
return new PreparedTurnOutcome(conversation, assistantContent, draftSucceeded, draftCancelled, draftFailure);
|
||||
}
|
||||
|
||||
private void ScheduleExecutionHistoryRender(bool autoScroll = true)
|
||||
{
|
||||
_pendingExecutionHistoryAutoScroll |= autoScroll;
|
||||
@@ -10287,10 +10344,29 @@ public partial class ChatWindow : Window
|
||||
{
|
||||
var logLevel = _settings.Settings.Llm.AgentLogLevel;
|
||||
|
||||
// Planning 이벤트는 단계 목록 카드로 별도 렌더링
|
||||
// Planning 이벤트는 기본적으로 얇은 요약만 보이고, debug에서만 큰 카드로 펼칩니다.
|
||||
if (evt.Type == AgentEventType.Planning && evt.Steps is { Count: > 0 })
|
||||
{
|
||||
AddPlanningCard(evt);
|
||||
if (string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddPlanningCard(evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
var compactPrimaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
var compactSecondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var compactHintBg = TryFindResource("HintBackground") as Brush
|
||||
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
var compactBorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
var compactAccentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue;
|
||||
var summary = !string.IsNullOrWhiteSpace(evt.Summary)
|
||||
? evt.Summary!
|
||||
: $"계획 {evt.Steps.Count}단계";
|
||||
var pill = CreateCompactEventPill(summary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush);
|
||||
pill.Opacity = 0;
|
||||
pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160)));
|
||||
MessagePanel.Children.Add(pill);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10876,64 +10952,15 @@ public partial class ChatWindow : Window
|
||||
private async Task SendRegenerateAsync(ChatConversation conv)
|
||||
{
|
||||
var runTab = NormalizeTabName(conv.Tab);
|
||||
_isStreaming = true;
|
||||
_streamRunTab = runTab;
|
||||
BtnSend.IsEnabled = false;
|
||||
BtnSend.Visibility = Visibility.Collapsed;
|
||||
BtnStop.Visibility = Visibility.Visible;
|
||||
_streamCts = new CancellationTokenSource();
|
||||
ForceScrollToEnd(); // 응답 시작 시 강제 하단 이동
|
||||
|
||||
var assistantContent = string.Empty;
|
||||
_activeStreamText = null;
|
||||
_cachedStreamContent = "";
|
||||
_displayedLength = 0;
|
||||
_cursorVisible = true;
|
||||
_aiIconPulseStopped = false;
|
||||
_streamStartTime = DateTime.UtcNow;
|
||||
_elapsedTimer.Start();
|
||||
SetStatus("에이전트 작업 중...", spinning: true);
|
||||
|
||||
try
|
||||
{
|
||||
List<ChatMessage> sendMessages;
|
||||
AxAgentExecutionEngine.PreparedExecution preparedExecution;
|
||||
lock (_convLock)
|
||||
preparedExecution = PrepareExecutionForConversation(conv, runTab, null);
|
||||
var executionMode = preparedExecution.Mode;
|
||||
sendMessages = preparedExecution.Messages;
|
||||
|
||||
var response = await _chatEngine.ExecutePreparedAsync(
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, runTab, conv, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
StopAiIconPulse();
|
||||
_cachedStreamContent = response;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
assistantContent = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true).Content;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
assistantContent = _chatEngine.FinalizeExecutionContent(assistantContent, ex).Content;
|
||||
ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetStreamingUiState();
|
||||
}
|
||||
|
||||
AxAgentExecutionEngine.PreparedExecution preparedExecution;
|
||||
lock (_convLock)
|
||||
{
|
||||
var session = ChatSession;
|
||||
assistantContent = _chatEngine.FinalizeAssistantTurn(session, conv, runTab, assistantContent, _storage);
|
||||
_currentConversation = session?.CurrentConversation ?? conv;
|
||||
conv = _currentConversation!;
|
||||
}
|
||||
FinalizeConversationTurn(conv.Tab ?? _activeTab, conv);
|
||||
preparedExecution = PrepareExecutionForConversation(conv, runTab, null);
|
||||
await ExecutePreparedTurnAsync(
|
||||
conv,
|
||||
runTab,
|
||||
conv.Tab ?? _activeTab,
|
||||
preparedExecution,
|
||||
"에이전트 작업 중...");
|
||||
}
|
||||
|
||||
/// <summary>채팅 본문 폭을 세 탭에서 동일한 기준으로 맞춥니다.</summary>
|
||||
|
||||
Reference in New Issue
Block a user