154 lines
5.5 KiB
C#
154 lines
5.5 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using AxCopilot.Services.Agent;
|
|
|
|
namespace AxCopilot.ViewModels;
|
|
|
|
/// <summary>Phase 18-B: 에이전트 이벤트 타임라인 재생 ViewModel.</summary>
|
|
public class ReplayTimelineViewModel : INotifyPropertyChanged
|
|
{
|
|
private readonly AgentReplayService _replayService;
|
|
private CancellationTokenSource? _replayCts;
|
|
|
|
public ObservableCollection<ReplayEventItem> Events { get; } = new();
|
|
public ObservableCollection<ReplaySessionInfo> Sessions { get; } = new();
|
|
|
|
private ReplaySessionInfo? _selectedSession;
|
|
public ReplaySessionInfo? SelectedSession
|
|
{
|
|
get => _selectedSession;
|
|
set { _selectedSession = value; OnPropertyChanged(); OnPropertyChanged(nameof(CanReplay)); }
|
|
}
|
|
|
|
private bool _isReplaying;
|
|
public bool IsReplaying
|
|
{
|
|
get => _isReplaying;
|
|
set { _isReplaying = value; OnPropertyChanged(); OnPropertyChanged(nameof(CanReplay)); }
|
|
}
|
|
|
|
public bool CanReplay => SelectedSession != null && !IsReplaying;
|
|
|
|
private int _replaySpeedMs = 200;
|
|
public int ReplaySpeedMs
|
|
{
|
|
get => _replaySpeedMs;
|
|
set { _replaySpeedMs = Math.Max(0, value); OnPropertyChanged(); }
|
|
}
|
|
|
|
private int _progressValue;
|
|
public int ProgressValue
|
|
{
|
|
get => _progressValue;
|
|
set { _progressValue = value; OnPropertyChanged(); }
|
|
}
|
|
|
|
private int _progressMax = 100;
|
|
public int ProgressMax
|
|
{
|
|
get => _progressMax;
|
|
set { _progressMax = value; OnPropertyChanged(); }
|
|
}
|
|
|
|
public ReplayTimelineViewModel()
|
|
{
|
|
_replayService = new AgentReplayService();
|
|
}
|
|
|
|
public void LoadSessions()
|
|
{
|
|
Sessions.Clear();
|
|
foreach (var s in _replayService.GetSessions())
|
|
Sessions.Add(s);
|
|
if (Sessions.Count > 0)
|
|
SelectedSession = Sessions[0];
|
|
}
|
|
|
|
public async Task StartReplayAsync()
|
|
{
|
|
if (SelectedSession == null || IsReplaying) return;
|
|
|
|
Events.Clear();
|
|
IsReplaying = true;
|
|
ProgressValue = 0;
|
|
_replayCts = new CancellationTokenSource();
|
|
|
|
try
|
|
{
|
|
// 전체 이벤트 수 먼저 파악
|
|
var allEvents = await _replayService.LoadAllEventsAsync(
|
|
SelectedSession.SessionId, _replayCts.Token);
|
|
ProgressMax = Math.Max(1, allEvents.Count);
|
|
|
|
int i = 0;
|
|
await _replayService.ReplayAsync(
|
|
SelectedSession.SessionId,
|
|
async record =>
|
|
{
|
|
var item = new ReplayEventItem(record);
|
|
// Dispatcher needed — raise event for UI thread
|
|
ReplayEventReceived?.Invoke(item);
|
|
i++;
|
|
ProgressValue = i;
|
|
await Task.CompletedTask;
|
|
},
|
|
ReplaySpeedMs,
|
|
_replayCts.Token);
|
|
}
|
|
catch (OperationCanceledException) { }
|
|
finally
|
|
{
|
|
IsReplaying = false;
|
|
_replayCts?.Dispose();
|
|
_replayCts = null;
|
|
}
|
|
}
|
|
|
|
public void StopReplay()
|
|
{
|
|
_replayCts?.Cancel();
|
|
}
|
|
|
|
/// <summary>UI 스레드에서 이벤트 항목을 추가하기 위한 이벤트.</summary>
|
|
public event Action<ReplayEventItem>? ReplayEventReceived;
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string? name = null)
|
|
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
}
|
|
|
|
/// <summary>타임라인에 표시할 단일 이벤트 항목.</summary>
|
|
public class ReplayEventItem
|
|
{
|
|
public AgentEventRecord Record { get; }
|
|
public string TypeLabel { get; }
|
|
public string Icon { get; }
|
|
public string TimeLabel { get; }
|
|
|
|
public ReplayEventItem(AgentEventRecord record)
|
|
{
|
|
Record = record;
|
|
TimeLabel = record.Timestamp.ToString("HH:mm:ss");
|
|
(TypeLabel, Icon) = record.Type switch
|
|
{
|
|
AgentEventLogType.SessionStart => ("세션 시작", "\uE768"),
|
|
AgentEventLogType.SessionEnd => ("세션 종료", "\uE711"),
|
|
AgentEventLogType.UserMessage => ("사용자 메시지", "\uE8BD"),
|
|
AgentEventLogType.AssistantMessage => ("AI 응답", "\uE8D4"),
|
|
AgentEventLogType.ToolRequest => ("도구 요청", "\uE756"),
|
|
AgentEventLogType.ToolResult => ("도구 결과", "\uE73E"),
|
|
AgentEventLogType.HookFired => ("훅 실행", "\uE81C"),
|
|
AgentEventLogType.HookResult => ("훅 결과", "\uE73E"),
|
|
AgentEventLogType.SkillActivated => ("스킬 활성화", "\uE82D"),
|
|
AgentEventLogType.SkillCompleted => ("스킬 완료", "\uE930"),
|
|
AgentEventLogType.CompactionTriggered => ("컨텍스트 압축", "\uE8EC"),
|
|
AgentEventLogType.CompactionCompleted => ("압축 완료", "\uE930"),
|
|
AgentEventLogType.SubagentSpawned => ("서브에이전트 생성", "\uE718"),
|
|
AgentEventLogType.SubagentCompleted => ("서브에이전트 완료", "\uE930"),
|
|
AgentEventLogType.ReflexionSaved => ("반성 저장", "\uE90F"),
|
|
AgentEventLogType.Error => ("오류", "\uE783"),
|
|
_ => (record.Type.ToString(), "\uE946")
|
|
};
|
|
}
|
|
}
|