AX Agent 무료티어 대기와 진행 표시 UX를 실제 동작 기준으로 정리
Some checks failed
Release Gate / gate (push) Has been cancelled

- Gemini 무료 티어 대기를 Gemini 서비스에서만 적용하도록 좁혀 vLLM/Ollama/Claude 작업이 불필요하게 멈추지 않게 수정
- 내부 설정과 빠른 설정의 Fast 표기를 Gemini 무료 티어 대기로 바꾸고 설명 문구도 실제 기능 기준으로 정리
- 단계 시작과 도구 호출 이벤트를 기본 transcript에 더 크게 노출하고 카드 배경/테두리/폰트 크기를 조정해 장시간 작업 중 상태를 읽기 쉽게 개선
- Cowork 장시간 무응답처럼 보이던 상황을 줄이기 위해 StepStart와 ToolCall이 더 이상 hover성 보조 정보처럼 숨지 않도록 수정

검증
- 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-06 22:18:29 +09:00
parent 36c04ccc07
commit f48e598cc1
6 changed files with 70 additions and 38 deletions

View File

@@ -466,11 +466,16 @@ public partial class AgentLoopService
EmitEvent(AgentEventType.Thinking, "", $"LLM에 요청 중... (반복 {iteration}/{maxIterations})");
// 무료 티어 모드: LLM 호출 간 딜레이 (RPM 한도 초과 방지)
if (llm.FreeTierMode && iteration > 1)
// Gemini 무료 티어 모드: LLM 호출 간 딜레이 (RPM 한도 초과 방지)
var activeService = (_settings.Settings.Llm.Service ?? "").Trim();
var shouldApplyFreeTierDelay =
llm.FreeTierMode
&& iteration > 1
&& string.Equals(activeService, "gemini", StringComparison.OrdinalIgnoreCase);
if (shouldApplyFreeTierDelay)
{
var delaySec = llm.FreeTierDelaySeconds > 0 ? llm.FreeTierDelaySeconds : 4;
EmitEvent(AgentEventType.Thinking, "", $"무료 티어 모드: {delaySec}초 대기 중...");
EmitEvent(AgentEventType.Thinking, "", $"Gemini 무료 티어 대기: {delaySec}초 후 다음 호출을 진행합니다...");
await Task.Delay(delaySec * 1000, ct);
}

View File

@@ -14,12 +14,12 @@ public partial class ChatWindow
{
return new Border
{
Background = Brushes.Transparent,
BorderBrush = Brushes.Transparent,
BorderThickness = new Thickness(0),
Background = hintBg,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(999),
Padding = new Thickness(6, 2, 6, 2),
Margin = new Thickness(8, 2, 220, 2),
Padding = new Thickness(8, 4, 8, 4),
Margin = new Thickness(8, 3, 180, 3),
HorizontalAlignment = HorizontalAlignment.Left,
Child = new StackPanel
{
@@ -30,16 +30,17 @@ public partial class ChatWindow
{
Text = "\uE9CE",
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 8,
FontSize = 9.5,
Foreground = accentBrush,
VerticalAlignment = VerticalAlignment.Center,
},
new TextBlock
{
Text = summary,
FontSize = 8.75,
Foreground = secondaryText,
Margin = new Thickness(4, 0, 0, 0),
FontSize = 9.5,
FontWeight = FontWeights.Medium,
Foreground = primaryText,
Margin = new Thickness(5, 0, 0, 0),
VerticalAlignment = VerticalAlignment.Center,
}
}
@@ -442,6 +443,19 @@ public partial class ChatWindow
if (evt.Type == AgentEventType.StepStart && evt.StepTotal > 0)
{
UpdateProgressBar(evt);
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 stepSummary = !string.IsNullOrWhiteSpace(evt.Summary)
? evt.Summary!
: $"단계 진행 중 ({evt.StepCurrent}/{evt.StepTotal})";
var pill = CreateCompactEventPill(stepSummary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush);
pill.Opacity = 0;
pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160)));
MessagePanel.Children.Add(pill);
return;
}
@@ -465,10 +479,6 @@ public partial class ChatWindow
&& evt.Type is AgentEventType.Paused or AgentEventType.Resumed)
return;
if (!string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase)
&& evt.Type == AgentEventType.ToolCall)
return;
var isTotalStats = evt.Type == AgentEventType.StepDone && evt.ToolName == "total_stats";
var transcriptBadgeLabel = GetTranscriptBadgeLabel(evt);
var permissionPresentation = evt.Type switch
@@ -522,12 +532,12 @@ public partial class ChatWindow
var banner = new Border
{
Background = Brushes.Transparent,
BorderBrush = Brushes.Transparent,
BorderThickness = new Thickness(0),
CornerRadius = new CornerRadius(0),
Padding = new Thickness(0),
Margin = new Thickness(12, 0, 12, 1),
Background = hintBg,
BorderBrush = borderColor,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(10),
Padding = new Thickness(9, 7, 9, 7),
Margin = new Thickness(12, 3, 12, 3),
HorizontalAlignment = HorizontalAlignment.Stretch,
};
if (!string.IsNullOrWhiteSpace(evt.RunId))
@@ -544,15 +554,15 @@ public partial class ChatWindow
{
Text = icon,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 8.25,
FontSize = 10,
Foreground = accentBrush,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 3, 0),
Margin = new Thickness(0, 0, 5, 0),
});
headerLeft.Children.Add(new TextBlock
{
Text = label,
FontSize = 8.25,
FontSize = 10,
FontWeight = FontWeights.Medium,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
@@ -562,7 +572,7 @@ public partial class ChatWindow
headerLeft.Children.Add(new TextBlock
{
Text = $" · {itemDisplayName}",
FontSize = 8.25,
FontSize = 10,
FontWeight = FontWeights.SemiBold,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center,
@@ -576,7 +586,7 @@ public partial class ChatWindow
headerRight.Children.Add(new TextBlock
{
Text = evt.ElapsedMs < 1000 ? $"{evt.ElapsedMs}ms" : $"{evt.ElapsedMs / 1000.0:F1}s",
FontSize = 7.5,
FontSize = 8.5,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(3, 0, 0, 0),
@@ -599,7 +609,7 @@ public partial class ChatWindow
Child = new TextBlock
{
Text = tokenText,
FontSize = 7.25,
FontSize = 8,
Foreground = secondaryText,
FontFamily = new FontFamily("Consolas"),
},
@@ -621,11 +631,11 @@ public partial class ChatWindow
sp.Children.Add(new TextBlock
{
Text = shortSummary,
FontSize = 8.4,
FontSize = 9.5,
Foreground = secondaryText,
TextWrapping = TextWrapping.NoWrap,
TextTrimming = TextTrimming.CharacterEllipsis,
Margin = new Thickness(11, 1, 0, 0),
Margin = new Thickness(15, 2, 0, 0),
});
}
}
@@ -635,10 +645,10 @@ public partial class ChatWindow
sp.Children.Add(new TextBlock
{
Text = summaryText,
FontSize = 8.4,
FontSize = 9.5,
Foreground = secondaryText,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(11, 1, 0, 0),
Margin = new Thickness(15, 2, 0, 0),
});
}

View File

@@ -3255,12 +3255,12 @@
<TextBlock Text="?" FontSize="10" FontWeight="Bold" Foreground="{DynamicResource AccentColor}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Border.ToolTip>
<ToolTip Style="{StaticResource HelpTooltipStyle}">
<TextBlock TextWrapping="Wrap" Foreground="White" FontSize="12" LineHeight="18" MaxWidth="280">호출 간격을 조정해 제한이 있는 환경에서 더 안정적으로 동작하게 합니다.</TextBlock>
<TextBlock TextWrapping="Wrap" Foreground="White" FontSize="12" LineHeight="18" MaxWidth="280">Gemini 무료 티어처럼 호출 제한이 있는 환경에서만 대기 시간을 둘 수 있습니다. 일반 서비스에서는 꺼두는 것을 권장합니다.</TextBlock>
</ToolTip>
</Border.ToolTip>
</Border>
</StackPanel>
<TextBlock Text="호출 간격을 조정해 제한이 있는 환경에서 더 안정적으로 동작합니다."
<TextBlock Text="Gemini 무료 티어처럼 호출 제한이 있는 환경에서만 대기 시간을 둡니다."
Margin="0,3,0,0"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"/>
@@ -3269,8 +3269,8 @@
Grid.Column="1"
Style="{StaticResource OverlayComboBox}"
SelectionChanged="CmbOverlayFastMode_SelectionChanged">
<ComboBoxItem Content="Fast · 켜짐" Tag="on"/>
<ComboBoxItem Content="Fast · 꺼짐" Tag="off"/>
<ComboBoxItem Content="Gemini 무료 티어 대기 · 켜짐" Tag="on"/>
<ComboBoxItem Content="Gemini 무료 티어 대기 · 꺼짐" Tag="off"/>
</ComboBox>
</Grid>
<Grid Margin="0,0,0,8">

View File

@@ -9723,7 +9723,7 @@ public partial class ChatWindow : Window
BuildInlineModelRows(models, llm.Model);
}
BtnInlineFastMode.Content = GetQuickActionLabel("Fast", llm.FreeTierMode ? "켜짐" : "꺼짐");
BtnInlineFastMode.Content = GetQuickActionLabel("Gemini 대기", llm.FreeTierMode ? "켜짐" : "꺼짐");
BtnInlineReasoning.Content = GetQuickActionLabel("추론", ReasoningLabel(llm.AgentDecisionLevel));
BtnInlinePermission.Content = GetQuickActionLabel("권한", PermissionModeCatalog.ToDisplayLabel(llm.FilePermission));
BtnInlineSkill.Content = $"스킬 · {(llm.EnableSkillSystem ? "On" : "Off")}";