Files

205 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace AxCopilot.Services;
public static class AgentStatsService
{
public record AgentSessionRecord
{
[JsonPropertyName("ts")]
public DateTime Timestamp { get; init; } = DateTime.Now;
[JsonPropertyName("tab")]
public string Tab { get; init; } = "";
[JsonPropertyName("model")]
public string Model { get; init; } = "";
[JsonPropertyName("calls")]
public int ToolCalls { get; init; }
[JsonPropertyName("ok")]
public int SuccessCount { get; init; }
[JsonPropertyName("fail")]
public int FailCount { get; init; }
[JsonPropertyName("itok")]
public int InputTokens { get; init; }
[JsonPropertyName("otok")]
public int OutputTokens { get; init; }
[JsonPropertyName("ms")]
public long DurationMs { get; init; }
[JsonPropertyName("tools")]
public List<string> UsedTools { get; init; } = new List<string>();
}
public record AgentStatsSummary
{
public int TotalSessions { get; init; }
public int TotalToolCalls { get; init; }
public int TotalTokens { get; init; }
public int TotalInputTokens { get; init; }
public int TotalOutputTokens { get; init; }
public long TotalDurationMs { get; init; }
public Dictionary<string, int> ToolFrequency { get; init; } = new Dictionary<string, int>();
public Dictionary<string, int> ModelBreakdown { get; init; } = new Dictionary<string, int>();
public Dictionary<string, int> TabBreakdown { get; init; } = new Dictionary<string, int>();
public Dictionary<string, int> DailySessions { get; init; } = new Dictionary<string, int>();
public Dictionary<string, int> DailyTokens { get; init; } = new Dictionary<string, int>();
}
private static readonly string StatsDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "stats");
private static readonly string StatsFile = Path.Combine(StatsDir, "agent_stats.json");
private static readonly JsonSerializerOptions _jsonOpts = new JsonSerializerOptions
{
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public static void RecordSession(AgentSessionRecord record)
{
Task.Run(delegate
{
try
{
if (!Directory.Exists(StatsDir))
{
Directory.CreateDirectory(StatsDir);
}
string text = JsonSerializer.Serialize(record, _jsonOpts);
File.AppendAllText(StatsFile, text + "\n");
}
catch (Exception ex)
{
LogService.Warn("통계 기록 실패: " + ex.Message);
}
});
}
public static AgentStatsSummary Aggregate(int days = 0)
{
List<AgentSessionRecord> records = LoadRecords(days);
return BuildSummary(records);
}
public static List<AgentSessionRecord> LoadRecords(int days = 0)
{
if (!File.Exists(StatsFile))
{
return new List<AgentSessionRecord>();
}
DateTime dateTime = ((days > 0) ? DateTime.Now.AddDays(-days) : DateTime.MinValue);
List<AgentSessionRecord> list = new List<AgentSessionRecord>();
foreach (string item in File.ReadLines(StatsFile))
{
if (string.IsNullOrWhiteSpace(item))
{
continue;
}
try
{
AgentSessionRecord agentSessionRecord = JsonSerializer.Deserialize<AgentSessionRecord>(item, _jsonOpts);
if (agentSessionRecord != null && agentSessionRecord.Timestamp >= dateTime)
{
list.Add(agentSessionRecord);
}
}
catch
{
}
}
return list;
}
private static AgentStatsSummary BuildSummary(List<AgentSessionRecord> records)
{
Dictionary<string, int> dictionary = new Dictionary<string, int>();
Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
Dictionary<string, int> dictionary3 = new Dictionary<string, int>();
Dictionary<string, int> dictionary4 = new Dictionary<string, int>();
Dictionary<string, int> dictionary5 = new Dictionary<string, int>();
int num = 0;
int num2 = 0;
int num3 = 0;
long num4 = 0L;
foreach (AgentSessionRecord record in records)
{
num += record.ToolCalls;
num2 += record.InputTokens;
num3 += record.OutputTokens;
num4 += record.DurationMs;
foreach (string usedTool in record.UsedTools)
{
dictionary[usedTool] = dictionary.GetValueOrDefault(usedTool) + 1;
}
if (!string.IsNullOrEmpty(record.Model))
{
dictionary2[record.Model] = dictionary2.GetValueOrDefault(record.Model) + 1;
}
if (!string.IsNullOrEmpty(record.Tab))
{
dictionary3[record.Tab] = dictionary3.GetValueOrDefault(record.Tab) + 1;
}
string key = record.Timestamp.ToString("yyyy-MM-dd");
dictionary4[key] = dictionary4.GetValueOrDefault(key) + 1;
dictionary5[key] = dictionary5.GetValueOrDefault(key) + record.InputTokens + record.OutputTokens;
}
return new AgentStatsSummary
{
TotalSessions = records.Count,
TotalToolCalls = num,
TotalTokens = num2 + num3,
TotalInputTokens = num2,
TotalOutputTokens = num3,
TotalDurationMs = num4,
ToolFrequency = dictionary.OrderByDescending((KeyValuePair<string, int> kv) => kv.Value).Take(10).ToDictionary((KeyValuePair<string, int> kv) => kv.Key, (KeyValuePair<string, int> kv) => kv.Value),
ModelBreakdown = dictionary2,
TabBreakdown = dictionary3,
DailySessions = dictionary4,
DailyTokens = dictionary5
};
}
public static long GetFileSize()
{
return File.Exists(StatsFile) ? new FileInfo(StatsFile).Length : 0;
}
public static void Clear()
{
try
{
if (File.Exists(StatsFile))
{
File.Delete(StatsFile);
}
}
catch (Exception ex)
{
LogService.Warn("통계 삭제 실패: " + ex.Message);
}
}
}