Skip to content

Commit 9feca0c

Browse files
authored
Merge pull request #153 from hchen2020/master
Add TokenStatistics for cost control.
2 parents c46fa84 + 6c052f8 commit 9feca0c

File tree

10 files changed

+79
-10
lines changed

10 files changed

+79
-10
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace BotSharp.Abstraction.Conversations;
2+
3+
public interface ITokenStatistics
4+
{
5+
int Total { get; }
6+
float AccumulatedCost { get; }
7+
float Cost { get; }
8+
void AddToken(int promptCount, int completionCount);
9+
void PrintStatistics();
10+
}

src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public class RoutingArgs
55
[JsonPropertyName("reason")]
66
public string Reason { get; set; } = string.Empty;
77

8-
[JsonPropertyName("agent_name")]
8+
[JsonPropertyName("agent")]
99
public string AgentName { get; set; } = string.Empty;
1010

1111
public override string ToString()

src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public static IServiceCollection AddBotSharp(this IServiceCollection services, I
6161
}
6262

6363
services.AddScoped<IInstructService, InstructService>();
64+
services.AddScoped<ITokenStatistics, TokenStatistics>();
6465

6566
return services;
6667
}

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ await routing.InstructLoop(agent) :
6363

6464
await HandleAssistantMessage(response, onMessageReceived);
6565

66+
var statistics = _services.GetRequiredService<ITokenStatistics>();
67+
statistics.PrintStatistics();
68+
6669
return true;
6770
}
6871

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Drawing;
2+
3+
namespace BotSharp.Core.Conversations.Services;
4+
5+
public class TokenStatistics : ITokenStatistics
6+
{
7+
private int _promptTokenCount = 0;
8+
private int _completionTokenCount = 0;
9+
private readonly IServiceProvider _services;
10+
private readonly ILogger _logger;
11+
public int Total => _promptTokenCount + _completionTokenCount;
12+
13+
public float Cost => _promptTokenCount / 1000f * 0.0015f + _completionTokenCount / 1000f * 0.002f;
14+
15+
public float AccumulatedCost
16+
{
17+
get
18+
{
19+
var stat = _services.GetRequiredService<IConversationStateService>();
20+
var promptTokenCount = int.Parse(stat.GetState("prompt_total", "0"));
21+
var completionTokenCount = int.Parse(stat.GetState("completion_total", "0"));
22+
return promptTokenCount / 1000f * 0.0015f + completionTokenCount / 1000f * 0.002f;
23+
}
24+
}
25+
26+
public TokenStatistics(IServiceProvider services, ILogger<TokenStatistics> logger)
27+
{
28+
_services = services;
29+
_logger = logger;
30+
}
31+
32+
public void AddToken(int promptCount, int completionCount)
33+
{
34+
_promptTokenCount += promptCount;
35+
_completionTokenCount += completionCount;
36+
37+
// Accumulated Token
38+
var stat = _services.GetRequiredService<IConversationStateService>();
39+
var count1 = int.Parse(stat.GetState("prompt_total", "0"));
40+
stat.SetState("prompt_total", promptCount + count1);
41+
var count2 = int.Parse(stat.GetState("completion_total", "0"));
42+
stat.SetState("completion_total", completionCount + count2);
43+
}
44+
45+
public void PrintStatistics()
46+
{
47+
#if DEBUG
48+
Console.WriteLine($"Token Usage: {_promptTokenCount} prompt + {_completionTokenCount} completion = {Total} total (${Cost}), accumulated cost: ${AccumulatedCost}", Color.DarkGray);
49+
#else
50+
_logger.LogInformation($"Token Usage: {_promptTokenCount} prompt + {_completionTokenCount} completion = {Total} total (${Cost}), accumulated cost: ${AccumulatedCost}");
51+
#endif
52+
}
53+
}

src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan
1414

1515
public List<NameDesc> Parameters => new List<NameDesc>
1616
{
17-
new NameDesc("agent_name", "the name of the agent"),
17+
new NameDesc("agent", "the name of the agent"),
1818
new NameDesc("args", "required parameters extracted from question"),
1919
new NameDesc("reason", "why continue to execute current task")
2020
};

src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
1414

1515
public List<NameDesc> Parameters => new List<NameDesc>
1616
{
17-
new NameDesc("agent_name", "the name of the agent"),
17+
new NameDesc("agent", "the name of the agent"),
1818
new NameDesc("question", "the question you will ask the agent to get the necessary data"),
1919
new NameDesc("reason", "why retrieve data"),
2020
new NameDesc("args", "required parameters extracted from question and hand over to the next agent")

src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
1515

1616
public List<NameDesc> Parameters => new List<NameDesc>
1717
{
18-
new NameDesc("agent_name", "the name of the agent from AGENTS"),
18+
new NameDesc("agent", "the name of the agent from AGENTS"),
1919
new NameDesc("reason", "why route to this agent"),
2020
new NameDesc("args", "parameters extracted from context")
2121
};

src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using BotSharp.Abstraction.Routing;
55
using BotSharp.Abstraction.Routing.Models;
66
using BotSharp.Abstraction.Routing.Settings;
7-
using BotSharp.Abstraction.Templating;
87
namespace BotSharp.Core.Routing;
98

109
public class RoutingService : IRoutingService
@@ -116,7 +115,8 @@ public Agent LoadRouter()
116115
var prompt = @"You're a Router with reasoning. Follow these steps to handle user's request:
117116
1. Read the CONVERSATION context.
118117
2. Select a appropriate function from FUNCTIONS.
119-
3. Determine which agent from AGENTS is suitable for the current task.";
118+
3. Determine which agent from AGENTS is suitable for the current task.
119+
4. Re-think about selected function is from FUNCTIONS to handle the request.";
120120

121121
// Append function
122122
prompt += "\r\n";
@@ -160,7 +160,7 @@ 2. Select a appropriate function from FUNCTIONS.
160160
// Append parameters
161161
if (agent.RequiredFields.Any())
162162
{
163-
prompt += $"\r\nRequired: {string.Join(',', agent.RequiredFields)}.";
163+
prompt += $"\r\nRequired: {string.Join(", ", agent.RequiredFields)}.";
164164
}
165165
return agent;
166166
}).ToList();

src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
using Microsoft.Extensions.Logging;
1313
using System;
1414
using System.Collections.Generic;
15-
using System.Drawing;
1615
using System.Linq;
1716
using System.Text.Json;
1817
using System.Threading.Tasks;
@@ -24,17 +23,20 @@ public class ChatCompletionProvider : IChatCompletion
2423
private readonly AzureOpenAiSettings _settings;
2524
private readonly IServiceProvider _services;
2625
private readonly ILogger _logger;
26+
private readonly ITokenStatistics _tokenStatistics;
2727
private string _model;
2828

2929
public virtual string Provider => "azure-openai";
3030

3131
public ChatCompletionProvider(AzureOpenAiSettings settings,
3232
ILogger<ChatCompletionProvider> logger,
33-
IServiceProvider services)
33+
IServiceProvider services,
34+
ITokenStatistics tokenStatistics)
3435
{
3536
_settings = settings;
3637
_logger = logger;
3738
_services = services;
39+
_tokenStatistics = tokenStatistics;
3840
}
3941

4042
protected virtual (OpenAIClient, string) GetClient()
@@ -105,7 +107,7 @@ public async Task<bool> GetChatCompletionsAsync(Agent agent,
105107
var choice = response.Value.Choices[0];
106108
var message = choice.Message;
107109

108-
_logger.LogInformation($"Token Usage: {response.Value.Usage.PromptTokens} prompt + {response.Value.Usage.CompletionTokens} completion = {response.Value.Usage.TotalTokens} total");
110+
_tokenStatistics.AddToken(response.Value.Usage.PromptTokens, response.Value.Usage.CompletionTokens);
109111

110112
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
111113
{

0 commit comments

Comments
 (0)