- claude-code compact 흐름을 참고해 AX에 session memory compact, microcompact, collapse/snip 단계를 추가하고 ContextCondenser를 단계별 결과 반환 구조로 확장함 - ChatConversation과 AX Agent 하단 컨텍스트 카드에 대화별 compact 누적 회수, 절감 토큰, session memory/microcompact/snip 집계를 반영함 - AGENTS.md에 개발 단계부터 최적화와 실행 속도를 우선 고려하는 작업 지침을 추가함 - 검증: 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:
@@ -99,6 +99,14 @@ public partial class ChatWindow : Window
|
||||
private int? _lastCompactionAfterTokens;
|
||||
private DateTime? _lastCompactionAt;
|
||||
private bool _lastCompactionWasAutomatic;
|
||||
private string _lastCompactionStageSummary = "";
|
||||
private int _sessionCompactionCount;
|
||||
private int _sessionAutomaticCompactionCount;
|
||||
private int _sessionManualCompactionCount;
|
||||
private int _sessionCompactionSavedTokens;
|
||||
private int _sessionMemoryCompactionCount;
|
||||
private int _sessionMicrocompactBoundaryCount;
|
||||
private int _sessionSnipCompactionCount;
|
||||
private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg)
|
||||
{
|
||||
if (button?.Content is not string text)
|
||||
@@ -1686,6 +1694,7 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
// 대화별 설정 복원 (없으면 전역 기본값)
|
||||
LoadConversationSettings();
|
||||
LoadCompactionMetricsFromConversation();
|
||||
UpdatePermissionUI();
|
||||
UpdateDataUsageUI();
|
||||
RefreshContextUsageVisual();
|
||||
@@ -1711,6 +1720,25 @@ public partial class ChatWindow : Window
|
||||
_selectedMood = conv?.Mood ?? llm.DefaultMood ?? "modern";
|
||||
}
|
||||
|
||||
private void LoadCompactionMetricsFromConversation()
|
||||
{
|
||||
ChatConversation? conv;
|
||||
lock (_convLock) conv = _currentConversation;
|
||||
|
||||
_sessionCompactionCount = conv?.CompactionCount ?? 0;
|
||||
_sessionAutomaticCompactionCount = conv?.AutomaticCompactionCount ?? 0;
|
||||
_sessionManualCompactionCount = conv?.ManualCompactionCount ?? 0;
|
||||
_sessionCompactionSavedTokens = conv?.CompactionSavedTokens ?? 0;
|
||||
_sessionMemoryCompactionCount = conv?.SessionMemoryCompactionCount ?? 0;
|
||||
_sessionMicrocompactBoundaryCount = conv?.MicrocompactBoundaryCount ?? 0;
|
||||
_sessionSnipCompactionCount = conv?.SnipCompactionCount ?? 0;
|
||||
_lastCompactionAt = conv?.LastCompactionAt;
|
||||
_lastCompactionWasAutomatic = conv?.LastCompactionWasAutomatic ?? false;
|
||||
_lastCompactionBeforeTokens = conv?.LastCompactionBeforeTokens;
|
||||
_lastCompactionAfterTokens = conv?.LastCompactionAfterTokens;
|
||||
_lastCompactionStageSummary = conv?.LastCompactionStageSummary ?? "";
|
||||
}
|
||||
|
||||
/// <summary>현재 하단 바 설정을 대화에 저장합니다.</summary>
|
||||
private void SaveConversationSettings()
|
||||
{
|
||||
@@ -6500,9 +6528,8 @@ public partial class ChatWindow : Window
|
||||
ForceScrollToEnd();
|
||||
|
||||
var llm = _settings.Settings.Llm;
|
||||
var beforeTokens = Services.TokenEstimator.EstimateMessages(conv.Messages);
|
||||
var working = conv.Messages.ToList();
|
||||
var condensed = await ContextCondenser.CondenseIfNeededAsync(
|
||||
var compactResult = await ContextCondenser.CondenseWithStatsAsync(
|
||||
working,
|
||||
_llm,
|
||||
llm.MaxContextTokens,
|
||||
@@ -6510,10 +6537,11 @@ public partial class ChatWindow : Window
|
||||
llm.ContextCompactTriggerPercent,
|
||||
true,
|
||||
CancellationToken.None);
|
||||
var afterTokens = Services.TokenEstimator.EstimateMessages(working);
|
||||
RecordCompactionStats(beforeTokens, afterTokens, wasAutomatic: false);
|
||||
var beforeTokens = compactResult.BeforeTokens;
|
||||
var afterTokens = compactResult.AfterTokens;
|
||||
RecordCompactionStats(compactResult, wasAutomatic: false);
|
||||
|
||||
if (condensed)
|
||||
if (compactResult.Changed)
|
||||
{
|
||||
lock (_convLock)
|
||||
{
|
||||
@@ -6521,8 +6549,8 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
var assistantText = condensed
|
||||
? $"컨텍스트 압축을 수행했습니다. 입력 토큰 추정치: {beforeTokens:N0} → {afterTokens:N0}"
|
||||
var assistantText = compactResult.Changed
|
||||
? $"컨텍스트 압축을 수행했습니다. 입력 토큰 추정치: {beforeTokens:N0} → {afterTokens:N0}\n- 단계: {compactResult.StageSummary}\n- 절감량: {compactResult.SavedTokens:N0} tokens"
|
||||
: "현재 대화는 압축할 충분한 이전 컨텍스트가 없어 변경 없이 유지했습니다.";
|
||||
|
||||
var assistantMsg = new ChatMessage { Role = "assistant", Content = assistantText };
|
||||
@@ -6547,7 +6575,7 @@ public partial class ChatWindow : Window
|
||||
ForceScrollToEnd();
|
||||
if (StatusTokens != null)
|
||||
StatusTokens.Text = $"컨텍스트 {Services.TokenEstimator.Format(beforeTokens)} → {Services.TokenEstimator.Format(afterTokens)}";
|
||||
SetStatus(condensed ? "컨텍스트 압축 완료" : "압축할 컨텍스트 없음", spinning: false);
|
||||
SetStatus(compactResult.Changed ? "컨텍스트 압축 완료" : "압축할 컨텍스트 없음", spinning: false);
|
||||
RefreshContextUsageVisual();
|
||||
RefreshConversationList();
|
||||
UpdateTaskSummaryIndicators();
|
||||
@@ -8081,8 +8109,7 @@ public partial class ChatWindow : Window
|
||||
// ── 전송 전 컨텍스트 사전 압축 ──
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
var beforeCompactTokens = Services.TokenEstimator.EstimateMessages(sendMessages);
|
||||
var condensed = await ContextCondenser.CondenseIfNeededAsync(
|
||||
var compactResult = await ContextCondenser.CondenseWithStatsAsync(
|
||||
sendMessages,
|
||||
_llm,
|
||||
llm.MaxContextTokens,
|
||||
@@ -8090,11 +8117,10 @@ public partial class ChatWindow : Window
|
||||
llm.ContextCompactTriggerPercent,
|
||||
false,
|
||||
_streamCts!.Token);
|
||||
if (condensed)
|
||||
if (compactResult.Changed)
|
||||
{
|
||||
var afterCompactTokens = Services.TokenEstimator.EstimateMessages(sendMessages);
|
||||
RecordCompactionStats(beforeCompactTokens, afterCompactTokens, wasAutomatic: true);
|
||||
SetStatus("컨텍스트를 사전 정리했습니다", spinning: true);
|
||||
RecordCompactionStats(compactResult, wasAutomatic: true);
|
||||
SetStatus($"컨텍스트를 사전 정리했습니다 · {compactResult.BeforeTokens:N0} → {compactResult.AfterTokens:N0} tokens", spinning: true);
|
||||
RefreshContextUsageVisual();
|
||||
}
|
||||
}
|
||||
@@ -16497,7 +16523,14 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
|
||||
? $"\n최근 압축: {(_lastCompactionWasAutomatic ? "자동" : "수동")} · {_lastCompactionAt.Value:HH:mm:ss}\n" +
|
||||
$"절감: {_lastCompactionBeforeTokens.Value:N0} → {_lastCompactionAfterTokens.Value:N0} tokens " +
|
||||
$"(-{Math.Max(0, _lastCompactionBeforeTokens.Value - _lastCompactionAfterTokens.Value):N0}, " +
|
||||
$"{Services.TokenEstimator.Format(_lastCompactionBeforeTokens.Value)} → {Services.TokenEstimator.Format(_lastCompactionAfterTokens.Value)})"
|
||||
$"{Services.TokenEstimator.Format(_lastCompactionBeforeTokens.Value)} → {Services.TokenEstimator.Format(_lastCompactionAfterTokens.Value)})\n" +
|
||||
$"단계: {(!string.IsNullOrWhiteSpace(_lastCompactionStageSummary) ? _lastCompactionStageSummary : "기본")}"
|
||||
: "";
|
||||
|
||||
var compactSession = _sessionCompactionCount > 0
|
||||
? $"\n세션 누적: {_sessionCompactionCount:N0}회 (자동 {_sessionAutomaticCompactionCount:N0} / 수동 {_sessionManualCompactionCount:N0})\n" +
|
||||
$"세션 절감: {Services.TokenEstimator.Format(_sessionCompactionSavedTokens)} tokens\n" +
|
||||
$"세션 메모리 {_sessionMemoryCompactionCount:N0}회 · 경계 {_sessionMicrocompactBoundaryCount:N0}건 · snip {_sessionSnipCompactionCount:N0}건"
|
||||
: "";
|
||||
|
||||
TokenUsageCard.ToolTip =
|
||||
@@ -16506,7 +16539,8 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
|
||||
$"간단 표기: {Services.TokenEstimator.Format(currentTokens)} / {Services.TokenEstimator.Format(maxContextTokens)}\n" +
|
||||
$"자동 압축 시작: {triggerPercent}%\n" +
|
||||
$"현재 입력 초안 포함" +
|
||||
compactHistory;
|
||||
compactHistory +
|
||||
compactSession;
|
||||
|
||||
UpdateCircularUsageArc(TokenUsageArc, usageRatio, 18, 18, 14);
|
||||
PositionThresholdMarker(TokenUsageThresholdMarker, triggerRatio, 18, 18, 14, 3);
|
||||
@@ -16555,12 +16589,45 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
|
||||
centerY + radius * Math.Sin(radians));
|
||||
}
|
||||
|
||||
private void RecordCompactionStats(int beforeTokens, int afterTokens, bool wasAutomatic)
|
||||
private void RecordCompactionStats(ContextCompactionResult result, bool wasAutomatic)
|
||||
{
|
||||
_lastCompactionBeforeTokens = Math.Max(0, beforeTokens);
|
||||
_lastCompactionAfterTokens = Math.Max(0, afterTokens);
|
||||
var beforeTokens = Math.Max(0, result.BeforeTokens);
|
||||
var afterTokens = Math.Max(0, result.AfterTokens);
|
||||
var savedTokens = Math.Max(0, beforeTokens - afterTokens);
|
||||
|
||||
_lastCompactionBeforeTokens = beforeTokens;
|
||||
_lastCompactionAfterTokens = afterTokens;
|
||||
_lastCompactionAt = DateTime.Now;
|
||||
_lastCompactionWasAutomatic = wasAutomatic;
|
||||
_lastCompactionStageSummary = result.StageSummary;
|
||||
_sessionCompactionCount++;
|
||||
_sessionCompactionSavedTokens += savedTokens;
|
||||
_sessionMemoryCompactionCount += result.SessionMemoryApplied ? 1 : 0;
|
||||
_sessionMicrocompactBoundaryCount += result.MicrocompactBoundaryCount;
|
||||
_sessionSnipCompactionCount += result.SnippedMessageCount + result.CollapsedBoundaryCount;
|
||||
if (wasAutomatic)
|
||||
_sessionAutomaticCompactionCount++;
|
||||
else
|
||||
_sessionManualCompactionCount++;
|
||||
|
||||
ChatConversation? conv;
|
||||
lock (_convLock) conv = _currentConversation;
|
||||
if (conv == null)
|
||||
return;
|
||||
|
||||
conv.CompactionCount = _sessionCompactionCount;
|
||||
conv.AutomaticCompactionCount = _sessionAutomaticCompactionCount;
|
||||
conv.ManualCompactionCount = _sessionManualCompactionCount;
|
||||
conv.CompactionSavedTokens = _sessionCompactionSavedTokens;
|
||||
conv.SessionMemoryCompactionCount = _sessionMemoryCompactionCount;
|
||||
conv.MicrocompactBoundaryCount = _sessionMicrocompactBoundaryCount;
|
||||
conv.SnipCompactionCount = _sessionSnipCompactionCount;
|
||||
conv.LastCompactionAt = _lastCompactionAt;
|
||||
conv.LastCompactionWasAutomatic = wasAutomatic;
|
||||
conv.LastCompactionBeforeTokens = beforeTokens;
|
||||
conv.LastCompactionAfterTokens = afterTokens;
|
||||
conv.LastCompactionStageSummary = _lastCompactionStageSummary;
|
||||
try { _storage.Save(conv); } catch { }
|
||||
}
|
||||
|
||||
private void ScheduleGitBranchRefresh(int delayMs = 400)
|
||||
|
||||
Reference in New Issue
Block a user