post-compaction 추적과 대화별 사용량 계측 보강
Some checks failed
Release Gate / gate (push) Has been cancelled

- claude-code의 post-compaction 플래그 흐름을 참고해 compact 직후 첫 응답을 별도로 추적하고 대화 단위 후속 사용량을 집계함

- ChatConversation에 pending post-compaction, 응답 회수, prompt/completion token 누적 필드를 추가하고 AX Agent 컨텍스트 카드 hover에 표시함

- README 및 docs/DEVELOPMENT.md에 2026-04-04 23:47 (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:
2026-04-04 23:54:59 +09:00
parent d2f8e39d2b
commit ca006972b2
4 changed files with 59 additions and 2 deletions

View File

@@ -175,6 +175,18 @@ public class ChatConversation
[JsonPropertyName("lastCompactionStageSummary")]
public string? LastCompactionStageSummary { get; set; }
[JsonPropertyName("pendingPostCompaction")]
public bool PendingPostCompaction { get; set; }
[JsonPropertyName("postCompactionResponseCount")]
public int PostCompactionResponseCount { get; set; }
[JsonPropertyName("postCompactionPromptTokens")]
public int PostCompactionPromptTokens { get; set; }
[JsonPropertyName("postCompactionCompletionTokens")]
public int PostCompactionCompletionTokens { get; set; }
}
public class ChatAgentRunRecord

View File

@@ -107,6 +107,10 @@ public partial class ChatWindow : Window
private int _sessionMemoryCompactionCount;
private int _sessionMicrocompactBoundaryCount;
private int _sessionSnipCompactionCount;
private bool _pendingPostCompaction;
private int _sessionPostCompactionResponseCount;
private int _sessionPostCompactionPromptTokens;
private int _sessionPostCompactionCompletionTokens;
private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg)
{
if (button?.Content is not string text)
@@ -1732,6 +1736,10 @@ public partial class ChatWindow : Window
_sessionMemoryCompactionCount = conv?.SessionMemoryCompactionCount ?? 0;
_sessionMicrocompactBoundaryCount = conv?.MicrocompactBoundaryCount ?? 0;
_sessionSnipCompactionCount = conv?.SnipCompactionCount ?? 0;
_pendingPostCompaction = conv?.PendingPostCompaction ?? false;
_sessionPostCompactionResponseCount = conv?.PostCompactionResponseCount ?? 0;
_sessionPostCompactionPromptTokens = conv?.PostCompactionPromptTokens ?? 0;
_sessionPostCompactionCompletionTokens = conv?.PostCompactionCompletionTokens ?? 0;
_lastCompactionAt = conv?.LastCompactionAt;
_lastCompactionWasAutomatic = conv?.LastCompactionWasAutomatic ?? false;
_lastCompactionBeforeTokens = conv?.LastCompactionBeforeTokens;
@@ -10939,10 +10947,12 @@ public partial class ChatWindow : Window
? _agentCumulativeOutputTokens
: usage?.CompletionTokens ?? 0;
var wasPostCompactionResponse = _pendingPostCompaction;
if (displayInput > 0 || displayOutput > 0)
{
UpdateStatusTokens(displayInput, displayOutput);
Services.UsageStatisticsService.RecordTokens(displayInput, displayOutput);
ConsumePostCompactionUsageIfNeeded(displayInput, displayOutput);
}
string tokenText;
if (displayInput > 0 || displayOutput > 0)
@@ -10952,9 +10962,10 @@ public partial class ChatWindow : Window
else
tokenText = $"~{FormatTokenCount(EstimateTokenCount(finalContent))} tokens";
var postCompactSuffix = wasPostCompactionResponse ? " · compact 직후" : "";
var metaText = new TextBlock
{
Text = $"{elapsedText} · {tokenText}",
Text = $"{elapsedText} · {tokenText}{postCompactSuffix}",
FontSize = 9.5,
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
HorizontalAlignment = HorizontalAlignment.Right,
@@ -16532,6 +16543,11 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
$"세션 절감: {Services.TokenEstimator.Format(_sessionCompactionSavedTokens)} tokens\n" +
$"세션 메모리 {_sessionMemoryCompactionCount:N0}회 · 경계 {_sessionMicrocompactBoundaryCount:N0}건 · snip {_sessionSnipCompactionCount:N0}건"
: "";
var postCompactionUsage = _sessionPostCompactionResponseCount > 0
? $"\ncompact 이후 응답: {_sessionPostCompactionResponseCount:N0}회\n" +
$"compact 이후 사용량: {Services.TokenEstimator.Format(_sessionPostCompactionPromptTokens)} + {Services.TokenEstimator.Format(_sessionPostCompactionCompletionTokens)} = {Services.TokenEstimator.Format(_sessionPostCompactionPromptTokens + _sessionPostCompactionCompletionTokens)} tokens"
: "";
var pendingPostCompaction = _pendingPostCompaction ? "\ncompact 후 첫 응답 대기 중" : "";
TokenUsageCard.ToolTip =
$"상태: {summary}\n" +
@@ -16540,7 +16556,9 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
$"자동 압축 시작: {triggerPercent}%\n" +
$"현재 입력 초안 포함" +
compactHistory +
compactSession;
compactSession +
postCompactionUsage +
pendingPostCompaction;
UpdateCircularUsageArc(TokenUsageArc, usageRatio, 18, 18, 14);
PositionThresholdMarker(TokenUsageThresholdMarker, triggerRatio, 18, 18, 14, 3);
@@ -16605,6 +16623,7 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
_sessionMemoryCompactionCount += result.SessionMemoryApplied ? 1 : 0;
_sessionMicrocompactBoundaryCount += result.MicrocompactBoundaryCount;
_sessionSnipCompactionCount += result.SnippedMessageCount + result.CollapsedBoundaryCount;
_pendingPostCompaction = true;
if (wasAutomatic)
_sessionAutomaticCompactionCount++;
else
@@ -16627,6 +16646,29 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
conv.LastCompactionBeforeTokens = beforeTokens;
conv.LastCompactionAfterTokens = afterTokens;
conv.LastCompactionStageSummary = _lastCompactionStageSummary;
conv.PendingPostCompaction = true;
try { _storage.Save(conv); } catch { }
}
private void ConsumePostCompactionUsageIfNeeded(int promptTokens, int completionTokens)
{
if (!_pendingPostCompaction)
return;
_pendingPostCompaction = false;
_sessionPostCompactionResponseCount++;
_sessionPostCompactionPromptTokens += Math.Max(0, promptTokens);
_sessionPostCompactionCompletionTokens += Math.Max(0, completionTokens);
ChatConversation? conv;
lock (_convLock) conv = _currentConversation;
if (conv == null)
return;
conv.PendingPostCompaction = false;
conv.PostCompactionResponseCount = _sessionPostCompactionResponseCount;
conv.PostCompactionPromptTokens = _sessionPostCompactionPromptTokens;
conv.PostCompactionCompletionTokens = _sessionPostCompactionCompletionTokens;
try { _storage.Save(conv); } catch { }
}