코워크·코드 장시간 실행 종료 직전 NullReference 실패를 방지하고 예외 추적을 보강한다
Some checks are pending
Release Gate / gate (push) Waiting to run
Some checks are pending
Release Gate / gate (push) Waiting to run
- ChatWindow 실행 경로에서 스트리밍 취소 토큰을 지역 변수로 고정해 마지막 라이브 프리뷰 단계에서 _streamCts null 참조가 발생하지 않도록 수정 - 최종 타이핑 프리뷰 컨테이너 준비 실패와 취소 예외를 방어적으로 처리해 장시간 작업이 마지막 UI 렌더 때문에 오류로 뒤집히지 않도록 정리 - 에이전트 실행 예외 전체를 앱 로그에 남기고 README 및 DEVELOPMENT 문서 이력을 갱신
This commit is contained in:
@@ -5595,7 +5595,9 @@ public partial class ChatWindow : Window
|
||||
BtnStop.Visibility = Visibility.Visible;
|
||||
if (runTab == "Cowork" || runTab == "Code")
|
||||
BtnPause.Visibility = Visibility.Visible;
|
||||
_streamCts = new CancellationTokenSource();
|
||||
var streamCts = new CancellationTokenSource();
|
||||
_streamCts = streamCts;
|
||||
var streamToken = streamCts.Token;
|
||||
ForceScrollToEnd();
|
||||
|
||||
var assistantContent = string.Empty;
|
||||
@@ -5651,8 +5653,8 @@ public partial class ChatWindow : Window
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, rememberTab, conversation, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
streamToken);
|
||||
assistantContent = response ?? string.Empty;
|
||||
}
|
||||
|
||||
responseElapsedMs = Math.Max(0, (long)(DateTime.UtcNow - _streamStartTime).TotalMilliseconds);
|
||||
@@ -5681,7 +5683,7 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
else if (preparedExecution.Mode.UseAgentLoop && !string.IsNullOrWhiteSpace(assistantContent))
|
||||
{
|
||||
await ShowTypedAssistantPreviewAsync(assistantContent, _streamCts.Token);
|
||||
await ShowTypedAssistantPreviewAsync(assistantContent, streamToken);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -5695,6 +5697,7 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.LogService.Debug($"에이전트 실행 실패: {ex}");
|
||||
var finalized = _chatEngine.FinalizeExecutionContentForUi(assistantContent, ex);
|
||||
assistantContent = finalized.Content;
|
||||
draftFailure = finalized.FailureReason;
|
||||
@@ -5734,6 +5737,9 @@ public partial class ChatWindow : Window
|
||||
return;
|
||||
|
||||
var container = CreateStreamingContainer(out var streamText);
|
||||
if (container == null || streamText == null || MessagePanel == null)
|
||||
return;
|
||||
|
||||
_activeStreamText = streamText;
|
||||
_cachedStreamContent = finalContent;
|
||||
_displayedLength = 0;
|
||||
@@ -5745,8 +5751,15 @@ public partial class ChatWindow : Window
|
||||
streamText.Text = _cursorVisible ? "\u258c" : " ";
|
||||
|
||||
var deadline = DateTime.UtcNow.AddMilliseconds(Math.Clamp(finalContent.Length * 6, 500, 1800));
|
||||
while (_displayedLength < _cachedStreamContent.Length && DateTime.UtcNow < deadline && !ct.IsCancellationRequested)
|
||||
await Task.Delay(30, ct);
|
||||
try
|
||||
{
|
||||
while (_displayedLength < _cachedStreamContent.Length && DateTime.UtcNow < deadline && !ct.IsCancellationRequested)
|
||||
await Task.Delay(30, ct);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_displayedLength = _cachedStreamContent.Length;
|
||||
if (_activeStreamText != null)
|
||||
|
||||
Reference in New Issue
Block a user