Skip to content

Commit 6141643

Browse files
committed
refine global stats
1 parent d7e42b0 commit 6141643

12 files changed

Lines changed: 160 additions & 187 deletions

File tree

src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,9 @@ List<string> GetInstructionLogSearchKeys(InstructLogKeysFilter filter)
187187
#endregion
188188

189189
#region Statistics
190-
BotSharpStats? GetGlobalStats(string metric, string dimension, string dimRefVal, DateTime recordTime, StatsInterval interval)
190+
BotSharpStats? GetGlobalStats(string agentId, DateTime recordTime, StatsInterval interval)
191191
=> throw new NotImplementedException();
192-
bool SaveGlobalStats(BotSharpStats body)
192+
bool SaveGlobalStats(BotSharpStatsDelta delta)
193193
=> throw new NotImplementedException();
194194

195195
#endregion

src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStats.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,23 @@ namespace BotSharp.Abstraction.Statistics.Models;
44

55
public class BotSharpStats
66
{
7-
[JsonPropertyName("metric")]
8-
public string Metric { get; set; } = null!;
7+
[JsonPropertyName("agent_id")]
8+
public string AgentId { get; set; } = null!;
99

10-
[JsonPropertyName("dimension")]
11-
public string Dimension { get; set; } = null!;
10+
[JsonPropertyName("agent_call_count")]
11+
public int AgentCallCount { get; set; }
1212

13-
[JsonPropertyName("dim_ref_val")]
14-
public string DimRefVal { get; set; } = null!;
13+
[JsonPropertyName("prompt_tokens")]
14+
public int PromptTokens { get; set; }
1515

16-
[JsonPropertyName("data")]
17-
public IDictionary<string, double> Data { get; set; } = new Dictionary<string, double>();
16+
[JsonPropertyName("completion_tokens")]
17+
public int CompletionTokens { get; set; }
18+
19+
[JsonPropertyName("prompt_total_cost")]
20+
public float PromptTotalCost { get; set; }
21+
22+
[JsonPropertyName("completion_total_cost")]
23+
public float CompletionTotalCost { get; set; }
1824

1925
[JsonPropertyName("record_time")]
2026
public DateTime RecordTime { get; set; } = DateTime.UtcNow;
@@ -44,11 +50,13 @@ public string Interval
4450
[JsonPropertyName("end_time")]
4551
public DateTime EndTime { get; set; }
4652

53+
4754
public override string ToString()
4855
{
49-
return $"{Metric}-{Dimension}-{DimRefVal} ({Interval}): {Data?.Count ?? 0}";
56+
return $"Stats: {AgentId}-{IntervalType}";
5057
}
5158

59+
5260
public static (DateTime, DateTime) BuildTimeInterval(DateTime recordTime, StatsInterval interval)
5361
{
5462
DateTime startTime = recordTime;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using BotSharp.Abstraction.Statistics.Enums;
2+
3+
namespace BotSharp.Abstraction.Statistics.Models;
4+
5+
public class BotSharpStatsDelta
6+
{
7+
public string AgentId { get; set; } = null!;
8+
public int AgentCallCountDelta { get; set; }
9+
public int PromptTokensDelta { get; set; }
10+
public int CompletionTokensDelta { get; set; }
11+
public float PromptTotalCostDelta { get; set; }
12+
public float CompletionTotalCostDelta { get; set; }
13+
public DateTime RecordTime { get; set; } = DateTime.UtcNow;
14+
public StatsInterval IntervalType { get; set; } = StatsInterval.Day;
15+
16+
public string Interval
17+
{
18+
get
19+
{
20+
return IntervalType.ToString();
21+
}
22+
set
23+
{
24+
if (Enum.TryParse(value, out StatsInterval type))
25+
{
26+
IntervalType = type;
27+
}
28+
}
29+
}
30+
}

src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStatsInput.cs

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/Infrastructure/BotSharp.Abstraction/Statistics/Models/StatsKeyValuePair.cs

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/Infrastructure/BotSharp.Abstraction/Statistics/Services/IBotSharpStatsService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ namespace BotSharp.Abstraction.Statistics.Services;
44

55
public interface IBotSharpStatsService
66
{
7-
bool UpdateStats(string resourceKey, BotSharpStatsInput input);
7+
bool UpdateStats(string @event, BotSharpStatsDelta delta);
88
}

src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,17 @@ public void AddToken(TokenStatsModel stats, RoleDialogModel message)
7373
var dim = "agent";
7474
var agentId = message.CurrentAgentId ?? string.Empty;
7575
var globalStats = _services.GetRequiredService<IBotSharpStatsService>();
76-
var body = new BotSharpStatsInput
76+
var delta = new BotSharpStatsDelta
7777
{
78-
Metric = metric,
79-
Dimension = dim,
80-
DimRefVal = agentId,
78+
AgentId = agentId,
8179
RecordTime = DateTime.UtcNow,
8280
IntervalType = StatsInterval.Day,
83-
Data = [
84-
new StatsKeyValuePair("prompt_token_count_total", stats.TotalInputTokens),
85-
new StatsKeyValuePair("completion_token_count_total", stats.TotalOutputTokens),
86-
new StatsKeyValuePair("prompt_cost_total", deltaPromptCost),
87-
new StatsKeyValuePair("completion_cost_total", deltaCompletionCost)
88-
]
81+
PromptTokensDelta = stats.TotalInputTokens,
82+
CompletionTokensDelta = stats.TotalOutputTokens,
83+
PromptTotalCostDelta = deltaPromptCost,
84+
CompletionTotalCostDelta = deltaCompletionCost
8985
};
90-
globalStats.UpdateStats($"global-{metric}-{dim}-{agentId}", body);
86+
globalStats.UpdateStats($"global-{metric}-{dim}-{agentId}", delta);
9187
}
9288

9389
public void PrintStatistics()

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Stats.cs

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,74 +4,100 @@ namespace BotSharp.Core.Repository;
44

55
public partial class FileRepository
66
{
7-
public BotSharpStats? GetGlobalStats(string metric, string dimension, string dimRefVal, DateTime recordTime, StatsInterval interval)
7+
public BotSharpStats? GetGlobalStats(string agentId, DateTime recordTime, StatsInterval interval)
88
{
9+
if (string.IsNullOrWhiteSpace(agentId))
10+
{
11+
return null;
12+
}
13+
914
var baseDir = Path.Combine(_dbSettings.FileRepository, STATS_FOLDER);
1015
var (startTime, endTime) = BotSharpStats.BuildTimeInterval(recordTime, interval);
11-
var dir = Path.Combine(baseDir, metric, startTime.Year.ToString(), startTime.Month.ToString("D2"));
12-
if (!Directory.Exists(dir)) return null;
16+
var dir = Path.Combine(baseDir, agentId, startTime.Year.ToString(), startTime.Month.ToString("D2"));
17+
if (!Directory.Exists(dir))
18+
{
19+
return null;
20+
}
1321

1422
var file = Directory.GetFiles(dir).FirstOrDefault(x => Path.GetFileName(x) == STATS_FILE);
15-
if (file == null) return null;
23+
if (file == null)
24+
{
25+
return null;
26+
}
1627

1728
var text = File.ReadAllText(file);
1829
var list = JsonSerializer.Deserialize<List<BotSharpStats>>(text, _options);
19-
var found = list?.FirstOrDefault(x => x.Metric.IsEqualTo(metric)
20-
&& x.Dimension.IsEqualTo(dimension)
21-
&& x.DimRefVal.IsEqualTo(dimRefVal)
30+
var found = list?.FirstOrDefault(x => x.AgentId.IsEqualTo(agentId)
2231
&& x.StartTime == startTime
2332
&& x.EndTime == endTime);
2433

2534
return found;
2635
}
2736

28-
public bool SaveGlobalStats(BotSharpStats body)
37+
public bool SaveGlobalStats(BotSharpStatsDelta delta)
2938
{
39+
if (delta == null || string.IsNullOrWhiteSpace(delta.AgentId))
40+
{
41+
return false;
42+
}
43+
3044
var baseDir = Path.Combine(_dbSettings.FileRepository, STATS_FOLDER);
31-
var (startTime, endTime) = BotSharpStats.BuildTimeInterval(body.RecordTime, body.IntervalType);
32-
body.StartTime = startTime;
33-
body.EndTime = endTime;
45+
var (startTime, endTime) = BotSharpStats.BuildTimeInterval(delta.RecordTime, delta.IntervalType);
3446

35-
var dir = Path.Combine(baseDir, body.Metric, startTime.Year.ToString(), startTime.Month.ToString("D2"));
47+
var dir = Path.Combine(baseDir, delta.AgentId, startTime.Year.ToString(), startTime.Month.ToString("D2"));
3648
if (!Directory.Exists(dir))
3749
{
3850
Directory.CreateDirectory(dir);
3951
}
4052

53+
var newItem = new BotSharpStats
54+
{
55+
AgentId = delta.AgentId,
56+
AgentCallCount = delta.AgentCallCountDelta,
57+
PromptTokens = delta.PromptTokensDelta,
58+
CompletionTokens = delta.CompletionTokensDelta,
59+
PromptTotalCost = delta.PromptTotalCostDelta,
60+
CompletionTotalCost = delta.CompletionTotalCostDelta,
61+
RecordTime = delta.RecordTime,
62+
StartTime = startTime,
63+
EndTime = endTime,
64+
Interval = delta.Interval
65+
};
66+
4167
var file = Path.Combine(dir, STATS_FILE);
4268
if (!File.Exists(file))
4369
{
44-
var list = new List<BotSharpStats> { body };
70+
var list = new List<BotSharpStats> { newItem };
4571
File.WriteAllText(file, JsonSerializer.Serialize(list, _options));
4672
}
4773
else
4874
{
4975
var text = File.ReadAllText(file);
5076
var list = JsonSerializer.Deserialize<List<BotSharpStats>>(text, _options);
51-
var found = list?.FirstOrDefault(x => x.Metric.IsEqualTo(body.Metric)
52-
&& x.Dimension.IsEqualTo(body.Dimension)
53-
&& x.DimRefVal.IsEqualTo(body.DimRefVal)
77+
var found = list?.FirstOrDefault(x => x.AgentId.IsEqualTo(delta.AgentId)
5478
&& x.StartTime == startTime
5579
&& x.EndTime == endTime);
5680

5781
if (found != null)
5882
{
59-
found.Metric = body.Metric;
60-
found.Dimension = body.Dimension;
61-
found.DimRefVal = body.DimRefVal;
62-
found.Data = body.Data;
63-
found.RecordTime = body.RecordTime;
64-
found.StartTime = body.StartTime;
65-
found.EndTime = body.EndTime;
66-
found.Interval = body.Interval;
83+
found.AgentId = delta.AgentId;
84+
found.RecordTime = delta.RecordTime;
85+
found.AgentCallCount += delta.AgentCallCountDelta;
86+
found.PromptTokens += delta.PromptTokensDelta;
87+
found.CompletionTokens += delta.CompletionTokensDelta;
88+
found.PromptTotalCost += delta.PromptTotalCostDelta;
89+
found.CompletionTotalCost += delta.CompletionTotalCostDelta;
90+
found.StartTime = startTime;
91+
found.EndTime = endTime;
92+
found.Interval = delta.Interval;
6793
}
6894
else if (list != null)
6995
{
70-
list.Add(body);
96+
list.Add(newItem);
7197
}
7298
else if (list == null)
7399
{
74-
list = new List<BotSharpStats> { body };
100+
list = [newItem];
75101
}
76102

77103
File.WriteAllText(file, JsonSerializer.Serialize(list, _options));

src/Infrastructure/BotSharp.Core/Statistics/Services/BotSharpStatsService.cs

Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ public class BotSharpStatsService : IBotSharpStatsService
99
private readonly ILogger<BotSharpStatsService> _logger;
1010
private readonly StatisticsSettings _settings;
1111

12-
private const int TIMEOUT_SECONDS = 5;
13-
1412
public BotSharpStatsService(
1513
IServiceProvider services,
1614
ILogger<BotSharpStatsService> logger,
@@ -22,74 +20,24 @@ public BotSharpStatsService(
2220
}
2321

2422

25-
public bool UpdateStats(string resourceKey, BotSharpStatsInput input)
23+
public bool UpdateStats(string @event, BotSharpStatsDelta delta)
2624
{
2725
try
2826
{
2927
if (!_settings.Enabled
30-
|| string.IsNullOrEmpty(resourceKey)
31-
|| input == null
32-
|| string.IsNullOrEmpty(input.Metric)
33-
|| string.IsNullOrEmpty(input.Dimension)
34-
|| string.IsNullOrEmpty(input.DimRefVal)
35-
|| input.Data.IsNullOrEmpty())
28+
|| delta == null
29+
|| string.IsNullOrEmpty(delta.AgentId))
3630
{
3731
return false;
3832
}
39-
40-
var locker = _services.GetRequiredService<IDistributedLocker>();
41-
var res = locker.Lock(resourceKey, () =>
42-
{
43-
var db = _services.GetRequiredService<IBotSharpRepository>();
44-
var body = db.GetGlobalStats(input.Metric, input.Dimension, input.DimRefVal, input.RecordTime, input.IntervalType);
45-
if (body == null)
46-
{
47-
var stats = new BotSharpStats
48-
{
49-
Metric = input.Metric,
50-
Dimension = input.Dimension,
51-
DimRefVal = input.DimRefVal,
52-
RecordTime = input.RecordTime,
53-
IntervalType = input.IntervalType,
54-
Data = input.Data.ToDictionary(x => x.Key, x => x.Value)
55-
};
56-
db.SaveGlobalStats(stats);
57-
return;
58-
}
59-
60-
foreach (var item in input.Data)
61-
{
62-
var curValue = item.Value;
63-
if (body.Data.TryGetValue(item.Key, out var preValue))
64-
{
65-
switch (item.Operation)
66-
{
67-
case StatsOperation.Add:
68-
preValue += curValue;
69-
break;
70-
case StatsOperation.Subtract:
71-
preValue -= curValue;
72-
break;
73-
case StatsOperation.Reset:
74-
preValue = 0;
75-
break;
76-
}
77-
body.Data[item.Key] = preValue;
78-
}
79-
else
80-
{
81-
body.Data[item.Key] = curValue;
82-
}
83-
}
84-
85-
db.SaveGlobalStats(body);
86-
}, TIMEOUT_SECONDS);
8733

88-
return res;
34+
var db = _services.GetRequiredService<IBotSharpRepository>();
35+
var isSaved = db.SaveGlobalStats(delta);
36+
return isSaved;
8937
}
9038
catch (Exception ex)
9139
{
92-
_logger.LogError(ex, $"Error when updating global stats {input.Metric}-{input.Dimension}-{input.DimRefVal}.");
40+
_logger.LogError(ex, $"Error when updating global stats {@event} (agent id: {delta?.AgentId}).");
9341
return false;
9442
}
9543
}

0 commit comments

Comments
 (0)