Skip to content

Commit 453d4bc

Browse files
author
Jicheng Lu
committed
refine global stats
1 parent a8fa46e commit 453d4bc

File tree

13 files changed

+118
-112
lines changed

13 files changed

+118
-112
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace BotSharp.Abstraction.Statistics.Enums;
22

3-
public static class StatCategory
3+
public static class StatsCategory
44
{
5-
public static string LlmCost = "llm-cost";
5+
public static string AgentLlmCost = "agent-llm-cost";
66
public static string AgentCall = "agent-call";
77
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace BotSharp.Abstraction.Statistics.Enums;
2+
3+
public enum StatsOperation
4+
{
5+
Add = 1,
6+
Subtract = 2,
7+
Reset = 3
8+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class BotSharpStats
99
public string Group { get; set; } = null!;
1010

1111
[JsonPropertyName("data")]
12-
public IDictionary<string, object> Data { get; set; } = new Dictionary<string, object>();
12+
public IDictionary<string, double> Data { get; set; } = new Dictionary<string, double>();
1313

1414
private DateTime innerRecordTime;
1515

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace BotSharp.Abstraction.Statistics.Models;
2+
3+
public class BotSharpStatsInput
4+
{
5+
public string Category { get; set; }
6+
public string Group { get; set; }
7+
public List<StatsKeyValuePair> Data { get; set; } = [];
8+
public DateTime RecordTime { get; set; }
9+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using BotSharp.Abstraction.Statistics.Enums;
2+
3+
namespace BotSharp.Abstraction.Statistics.Models;
4+
5+
public class StatsKeyValuePair
6+
{
7+
public string Key { get; set; }
8+
public double Value { get; set; }
9+
public StatsOperation Operation { get; set; }
10+
11+
public StatsKeyValuePair()
12+
{
13+
14+
}
15+
16+
public StatsKeyValuePair(string key, double value, StatsOperation operation = StatsOperation.Add)
17+
{
18+
Key = key;
19+
Value = value;
20+
Operation = operation;
21+
}
22+
23+
public override string ToString()
24+
{
25+
return $"[{Key}]: {Value} ({Operation})";
26+
}
27+
}

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

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

55
public interface IBotSharpStatsService
66
{
7-
bool UpdateLlmCost(BotSharpStats stats);
8-
bool UpdateAgentCall(BotSharpStats stats);
7+
bool UpdateStats(string resourceKey, BotSharpStatsInput input);
98
}

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

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,19 @@ public void AddToken(TokenStatsModel stats, RoleDialogModel message)
6060

6161

6262
var globalStats = _services.GetRequiredService<IBotSharpStatsService>();
63-
var body = new BotSharpStats
63+
var body = new BotSharpStatsInput
6464
{
65-
Category = StatCategory.LlmCost,
66-
Group = $"Agent: {message.CurrentAgentId}",
67-
Data = new Dictionary<string, object>
68-
{
69-
{ "prompt_token_count_total", stats.PromptCount },
70-
{ "completion_token_count_total", stats.CompletionCount },
71-
{ "prompt_cost_total", deltaPromptCost },
72-
{ "completion_cost_total", deltaCompletionCost }
73-
},
74-
RecordTime = DateTime.UtcNow
65+
Category = StatsCategory.AgentLlmCost,
66+
Group = message.CurrentAgentId,
67+
RecordTime = DateTime.UtcNow,
68+
Data = [
69+
new StatsKeyValuePair("prompt_token_count_total", stats.PromptCount),
70+
new StatsKeyValuePair("completion_token_count_total", stats.CompletionCount),
71+
new StatsKeyValuePair("prompt_cost_total", deltaPromptCost),
72+
new StatsKeyValuePair("completion_cost_total", deltaCompletionCost)
73+
]
7574
};
76-
globalStats.UpdateLlmCost(body);
75+
globalStats.UpdateStats("global-llm-cost", body);
7776
}
7877

7978
public void PrintStatistics()

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ public partial class FileRepository
1313
var file = Directory.GetFiles(dir).FirstOrDefault(x => Path.GetFileName(x) == STATS_FILE);
1414
if (file == null) return null;
1515

16+
var time = BuildRecordTime(recordTime);
1617
var text = File.ReadAllText(file);
1718
var list = JsonSerializer.Deserialize<List<BotSharpStats>>(text, _options);
1819
var found = list?.FirstOrDefault(x => x.Category.IsEqualTo(category)
1920
&& x.Group.IsEqualTo(group)
20-
&& x.RecordTime == recordTime);
21+
&& x.RecordTime == time);
2122
return found;
2223
}
2324

@@ -38,11 +39,12 @@ public bool SaveGlobalStats(BotSharpStats body)
3839
}
3940
else
4041
{
42+
var time = BuildRecordTime(body.RecordTime);
4143
var text = File.ReadAllText(file);
4244
var list = JsonSerializer.Deserialize<List<BotSharpStats>>(text, _options);
4345
var found = list?.FirstOrDefault(x => x.Category.IsEqualTo(body.Category)
4446
&& x.Group.IsEqualTo(body.Group)
45-
&& x.RecordTime == body.RecordTime);
47+
&& x.RecordTime == time);
4648

4749
if (found != null)
4850
{
@@ -65,4 +67,12 @@ public bool SaveGlobalStats(BotSharpStats body)
6567

6668
return true;
6769
}
70+
71+
#region Private methods
72+
private DateTime BuildRecordTime(DateTime date)
73+
{
74+
var recordDate = new DateTime(date.Year, date.Month, date.Day, date.Hour, 0, 0);
75+
return DateTime.SpecifyKind(recordDate, DateTimeKind.Utc);
76+
}
77+
#endregion
6878
}

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

Lines changed: 37 additions & 77 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 string GLOBAL_LLM_COST = "global-llm-cost";
13-
private const string GLOBAL_AGENT_CALL = "global-agent-call";
1412
private const int TIMEOUT_SECONDS = 5;
1513

1614
public BotSharpStatsService(
@@ -23,109 +21,71 @@ public BotSharpStatsService(
2321
_settings = settings;
2422
}
2523

26-
public bool UpdateLlmCost(BotSharpStats stats)
24+
25+
public bool UpdateStats(string resourceKey, BotSharpStatsInput input)
2726
{
2827
try
2928
{
30-
if (!_settings.Enabled) return false;
31-
32-
var db = _services.GetRequiredService<IBotSharpRepository>();
29+
if (!_settings.Enabled
30+
|| string.IsNullOrEmpty(resourceKey)
31+
|| input == null
32+
|| string.IsNullOrEmpty(input.Category)
33+
|| string.IsNullOrEmpty(input.Group))
34+
{
35+
return false;
36+
}
37+
3338
var locker = _services.GetRequiredService<IDistributedLocker>();
34-
35-
var res = locker.Lock(GLOBAL_LLM_COST, () =>
39+
var res = locker.Lock(resourceKey, () =>
3640
{
37-
var body = db.GetGlobalStats(stats.Category, stats.Group, stats.RecordTime);
41+
var db = _services.GetRequiredService<IBotSharpRepository>();
42+
var body = db.GetGlobalStats(input.Category, input.Group, input.RecordTime);
3843
if (body == null)
3944
{
45+
var stats = new BotSharpStats
46+
{
47+
Category = input.Category,
48+
Group = input.Group,
49+
RecordTime = input.RecordTime,
50+
Data = input.Data.ToDictionary(x => x.Key, x => x.Value)
51+
};
4052
db.SaveGlobalStats(stats);
4153
return;
4254
}
4355

44-
foreach (var item in stats.Data)
56+
foreach (var item in input.Data)
4557
{
4658
var curValue = item.Value;
4759
if (body.Data.TryGetValue(item.Key, out var preValue))
4860
{
49-
var preValStr = preValue?.ToString();
50-
var curValStr = curValue?.ToString();
51-
try
61+
switch (item.Operation)
5262
{
53-
if (int.TryParse(preValStr, out var count))
54-
{
55-
curValue = int.Parse(curValStr ?? "0") + count;
56-
}
57-
else if (double.TryParse(preValStr, out var num))
58-
{
59-
curValue = double.Parse(curValStr ?? "0") + num;
60-
}
61-
}
62-
catch
63-
{
64-
continue;
63+
case StatsOperation.Add:
64+
preValue += curValue;
65+
break;
66+
case StatsOperation.Subtract:
67+
preValue -= curValue;
68+
break;
69+
case StatsOperation.Reset:
70+
preValue = 0;
71+
break;
6572
}
73+
body.Data[item.Key] = preValue;
6674
}
67-
68-
body.Data[item.Key] = curValue;
69-
}
70-
71-
db.SaveGlobalStats(body);
72-
}, TIMEOUT_SECONDS);
73-
return res;
74-
}
75-
catch (Exception ex)
76-
{
77-
_logger.LogError($"Error when updating global llm cost stats {stats}. {ex.Message}\r\n{ex.InnerException}");
78-
return false;
79-
}
80-
}
81-
82-
public bool UpdateAgentCall(BotSharpStats stats)
83-
{
84-
try
85-
{
86-
if (!_settings.Enabled) return false;
87-
88-
var db = _services.GetRequiredService<IBotSharpRepository>();
89-
var locker = _services.GetRequiredService<IDistributedLocker>();
90-
91-
var res = locker.Lock(GLOBAL_AGENT_CALL, () =>
92-
{
93-
var body = db.GetGlobalStats(stats.Category, stats.Group, stats.RecordTime);
94-
if (body == null)
95-
{
96-
db.SaveGlobalStats(stats);
97-
return;
98-
}
99-
100-
foreach (var item in stats.Data)
101-
{
102-
var curValue = item.Value;
103-
if (body.Data.TryGetValue(item.Key, out var preValue))
75+
else
10476
{
105-
var preValStr = preValue?.ToString();
106-
var curValStr = curValue?.ToString();
107-
try
108-
{
109-
if (int.TryParse(preValStr, out var count))
110-
{
111-
curValue = int.Parse(curValStr ?? "0") + count;
112-
}
113-
}
114-
catch
115-
{
116-
continue;
117-
}
77+
body.Data[item.Key] = curValue;
11878
}
119-
body.Data[item.Key] = curValue;
12079
}
12180

12281
db.SaveGlobalStats(body);
12382
}, TIMEOUT_SECONDS);
83+
12484
return res;
12585
}
12686
catch (Exception ex)
12787
{
128-
_logger.LogError($"Error when updating global agent call stats {stats}. {ex.Message}\r\n{ex.InnerException}");
88+
_logger.LogError($"Error when updating global stats {input.Category}-{input.Group}. {ex.Message}\r\n{ex.InnerException}");
12989
return false;
13090
}
13191
}

src/Infrastructure/BotSharp.Logger/Hooks/GlobalStatsConversationHook.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,15 @@ private void UpdateAgentCall(RoleDialogModel message)
2929
// record agent call
3030
var globalStats = _services.GetRequiredService<IBotSharpStatsService>();
3131

32-
var body = new BotSharpStats
32+
var body = new BotSharpStatsInput
3333
{
34-
Category = StatCategory.AgentCall,
35-
Group = $"Agent: {message.CurrentAgentId}",
36-
Data = new Dictionary<string, object>
37-
{
38-
{ "agent_id", message.CurrentAgentId },
39-
{ "agent_call_count", 1 }
40-
},
34+
Category = StatsCategory.AgentCall,
35+
Group = message.CurrentAgentId,
36+
Data = [
37+
new StatsKeyValuePair("agent_call_count", 1)
38+
],
4139
RecordTime = DateTime.UtcNow
4240
};
43-
globalStats.UpdateAgentCall(body);
41+
globalStats.UpdateStats("global-agent-call", body);
4442
}
4543
}

0 commit comments

Comments
 (0)