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

127 lines
4.6 KiB
C#
Raw Normal View History

2024-04-02 19:24:04 +00:00
using BotSharp.Abstraction.Conversations.Enums;
2023-12-13 18:12:25 +00:00
using BotSharp.Abstraction.MLTasks;
using System.Diagnostics;
2023-09-24 21:32:58 +00:00
namespace BotSharp.Core.Conversations.Services;
public class TokenStatistics : ITokenStatistics
{
private int _promptTokenCount = 0;
2023-09-26 03:04:04 +00:00
private float _promptCost = 0f;
2023-09-24 21:32:58 +00:00
private int _completionTokenCount = 0;
2023-09-26 03:04:04 +00:00
private float _completionCost = 0f;
2023-09-24 21:32:58 +00:00
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public int Total => _promptTokenCount + _completionTokenCount;
2023-09-26 03:04:04 +00:00
public string _model;
private Stopwatch _timer;
2023-09-24 21:32:58 +00:00
2023-09-26 03:04:04 +00:00
public float Cost => _promptCost + _completionCost;
2023-09-24 21:32:58 +00:00
public float AccumulatedCost
{
get
{
var stat = _services.GetRequiredService<IConversationStateService>();
2023-09-26 03:04:04 +00:00
return float.Parse(stat.GetState("llm_total_cost", "0"));
2023-09-24 21:32:58 +00:00
}
}
public TokenStatistics(IServiceProvider services, ILogger<TokenStatistics> logger)
{
_services = services;
_logger = logger;
}
2025-01-24 21:22:23 +00:00
public void AddToken(TokenStatsModel stats, RoleDialogModel message)
2023-09-24 21:32:58 +00:00
{
2023-09-26 03:04:04 +00:00
_model = stats.Model;
_promptTokenCount += stats.PromptCount;
_completionTokenCount += stats.CompletionCount;
2023-12-13 18:12:25 +00:00
var settingsService = _services.GetRequiredService<ILlmProviderService>();
2023-12-13 18:12:25 +00:00
var settings = settingsService.GetSetting(stats.Provider, _model);
2025-03-21 22:34:00 +00:00
var deltaPromptCost = (stats.PromptCount - stats.CachedPromptCount) / 1000f * settings.PromptCost;
var deltaCachedPromptCost = stats.CachedPromptCount / 1000f * (settings.AdditionalCost?.CachedPromptCost ?? 0f);
2025-01-29 20:07:51 +00:00
var deltaCompletionCost = stats.CompletionCount / 1000f * settings.CompletionCost;
2025-03-21 22:32:29 +00:00
var deltaTotal = deltaPromptCost + deltaCachedPromptCost + deltaCompletionCost;
2025-01-24 05:45:43 +00:00
_promptCost += deltaPromptCost;
_completionCost += deltaCompletionCost;
2023-09-24 21:32:58 +00:00
// Accumulated Token
var stat = _services.GetRequiredService<IConversationStateService>();
2023-12-13 18:12:25 +00:00
var inputCount = int.Parse(stat.GetState("prompt_total", "0"));
2024-04-02 19:24:04 +00:00
stat.SetState("prompt_total", stats.PromptCount + inputCount, isNeedVersion: false, source: StateSource.Application);
2023-12-13 18:12:25 +00:00
var outputCount = int.Parse(stat.GetState("completion_total", "0"));
2024-04-02 19:24:04 +00:00
stat.SetState("completion_total", stats.CompletionCount + outputCount, isNeedVersion: false, source: StateSource.Application);
2025-03-21 22:34:00 +00:00
var cachedCount = int.Parse(stat.GetState("cached_prompt_total", "0"));
stat.SetState("cached_prompt_total", stats.CachedPromptCount + cachedCount, isNeedVersion: false, source: StateSource.Application);
2023-09-26 03:04:04 +00:00
// Total cost
2023-12-13 18:12:25 +00:00
var total_cost = float.Parse(stat.GetState("llm_total_cost", "0"));
2025-01-29 20:07:51 +00:00
total_cost += deltaTotal;
2024-04-02 19:24:04 +00:00
stat.SetState("llm_total_cost", total_cost, isNeedVersion: false, source: StateSource.Application);
2025-01-24 05:45:43 +00:00
2025-01-29 20:07:51 +00:00
// Save stats
2025-02-28 21:34:53 +00:00
var metric = StatsMetric.AgentLlmCost;
var dim = "agent";
2025-02-28 16:00:58 +00:00
var agentId = message.CurrentAgentId ?? string.Empty;
2025-01-28 00:23:33 +00:00
var globalStats = _services.GetRequiredService<IBotSharpStatsService>();
2025-01-28 05:10:37 +00:00
var body = new BotSharpStatsInput
2025-01-24 05:45:43 +00:00
{
2025-02-28 21:34:53 +00:00
Metric = metric,
Dimension = dim,
2025-02-28 16:00:58 +00:00
DimRefVal = agentId,
2025-01-28 05:10:37 +00:00
RecordTime = DateTime.UtcNow,
2025-01-28 22:46:59 +00:00
IntervalType = StatsInterval.Day,
2025-01-28 05:10:37 +00:00
Data = [
new StatsKeyValuePair("prompt_token_count_total", stats.PromptCount),
new StatsKeyValuePair("completion_token_count_total", stats.CompletionCount),
new StatsKeyValuePair("prompt_cost_total", deltaPromptCost),
new StatsKeyValuePair("completion_cost_total", deltaCompletionCost)
]
2025-01-24 05:45:43 +00:00
};
2025-02-28 21:35:46 +00:00
globalStats.UpdateStats($"global-{metric}-{dim}-{agentId}", body);
2023-09-24 21:32:58 +00:00
}
public void PrintStatistics()
{
2024-01-13 21:17:40 +00:00
if (_timer == null)
{
_timer = Stopwatch.StartNew();
}
else
{
_timer.Start();
}
var stats = $"Token Usage: {_promptTokenCount} prompt + {_completionTokenCount} completion = {Total} total tokens ({_timer.ElapsedMilliseconds / 1000f:f2}s). One-Way cost: {Cost:C4}, accumulated cost: {AccumulatedCost:C4}. [{_model}]";
2023-09-24 21:32:58 +00:00
#if DEBUG
2024-07-03 19:11:13 +00:00
Console.WriteLine(stats);
2023-09-24 21:32:58 +00:00
#else
2023-09-26 03:04:04 +00:00
_logger.LogInformation(stats);
2023-09-24 21:32:58 +00:00
#endif
}
public void StartTimer()
{
if (_timer == null)
{
_timer = Stopwatch.StartNew();
}
else
{
_timer.Start();
}
}
public void StopTimer()
{
2025-02-28 18:44:59 +00:00
if (_timer == null)
{
return;
}
_timer.Stop();
}
2023-09-24 21:32:58 +00:00
}