Skip to content

Commit 3caa7a5

Browse files
committed
HFPlanner
1 parent 8bf61cb commit 3caa7a5

File tree

39 files changed

+338
-213
lines changed

39 files changed

+338
-213
lines changed

docs/architecture/hooks.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,16 @@ More information about conversation hook please go to [Conversation Hook](../con
4747
```csharp
4848
Task OnStateLoaded(ConversationState state);
4949
Task OnStateChanged(string name, string preValue, string currentValue);
50+
```
51+
52+
### Content Generating Hook
53+
`IContentGeneratingHook`
54+
55+
Model content generating hook, it can be used for logging, metrics and tracing.
56+
```csharp
57+
// Before content generating.
58+
Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations);
59+
60+
// After content generated.
61+
Task AfterGenerated(RoleDialogModel message, TokenStatsModel tokenStats);
5062
```

src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace BotSharp.Abstraction.Conversations;
33
public interface IConversationService
44
{
55
IConversationStateService States { get; }
6+
string ConversationId { get; }
67
Task<Conversation> NewConversation(Conversation conversation);
78
void SetConversationId(string conversationId, List<string> states);
89
Task<Conversation> GetConversation(string id);

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using BotSharp.Abstraction.Functions.Models;
2-
using BotSharp.Abstraction.Models;
32

43
namespace BotSharp.Abstraction.Conversations.Models;
54

65
public class RoleDialogModel : ITrackableMessage
76
{
7+
/// <summary>
8+
/// If Role is Assistant, it is same as user's message id.
9+
/// </summary>
810
public string MessageId { get; set; }
911

1012
/// <summary>

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/TokenStatsModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace BotSharp.Abstraction.Conversations.Models;
33
public class TokenStatsModel
44
{
55
public string Model { get; set; }
6+
public string Prompt { get; set; }
67
public int PromptCount { get; set; }
78
public int CompletionCount { get; set; }
89

src/Infrastructure/BotSharp.Abstraction/MLTasks/ITextCompletion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public interface ITextCompletion
1313
/// <param name="model"></param>
1414
void SetModelName(string model);
1515

16-
Task<string> GetCompletion(string text);
16+
Task<string> GetCompletion(string text, string agentId, string messageId);
1717
}

src/Infrastructure/BotSharp.Abstraction/Planning/IExecutor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ namespace BotSharp.Abstraction.Planning;
55

66
public interface IExecutor
77
{
8-
Task<bool> Execute(IRoutingService routing,
9-
Agent router,
8+
Task<RoleDialogModel> Execute(IRoutingService routing,
109
FunctionCallFromLlm inst,
11-
List<RoleDialogModel> dialogs,
12-
RoleDialogModel message);
10+
RoleDialogModel message,
11+
List<RoleDialogModel> dialogs);
1312
}

src/Infrastructure/BotSharp.Abstraction/Planning/IPlaner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace BotSharp.Abstraction.Planning;
77
/// </summary>
88
public interface IPlaner
99
{
10-
Task<FunctionCallFromLlm> GetNextInstruction(Agent router);
10+
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId);
1111
Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message);
1212
Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message);
1313
}

src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHandler.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using BotSharp.Abstraction.Functions.Models;
2-
using BotSharp.Abstraction.Planning;
32

43
namespace BotSharp.Abstraction.Routing;
54

@@ -15,9 +14,7 @@ public interface IRoutingHandler
1514
bool Enabled => true;
1615
List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>();
1716

18-
void SetRouter(Agent router) { }
19-
20-
void SetDialogs(List<RoleDialogModel> dialogs) { }
17+
void SetDialogs(List<RoleDialogModel> dialogs);
2118

2219
Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message);
2320
}

src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ namespace BotSharp.Abstraction.Routing;
22

33
public interface IRoutingService
44
{
5-
List<RoleDialogModel> Dialogs { get; }
5+
Agent Router { get; }
66
void ResetRecursiveCounter();
7-
void RefreshDialogs();
8-
Task<bool> InvokeAgent(string agentId, RoleDialogModel message);
9-
Task<bool> InstructLoop(RoleDialogModel message);
10-
Task<bool> ExecuteOnce(Agent agent, RoleDialogModel message);
7+
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);
8+
Task<RoleDialogModel> InstructLoop(RoleDialogModel message);
9+
Task<RoleDialogModel> ExecuteOnce(Agent agent, RoleDialogModel message);
1110
}

src/Infrastructure/BotSharp.Abstraction/Routing/RoutingHandlerBase.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ namespace BotSharp.Abstraction.Routing;
55

66
public abstract class RoutingHandlerBase
77
{
8-
protected Agent _router;
98
protected readonly IServiceProvider _services;
109
protected readonly ILogger _logger;
1110
protected RoutingSettings _settings;
@@ -20,11 +19,6 @@ public RoutingHandlerBase(IServiceProvider services,
2019
_settings = settings;
2120
}
2221

23-
public void SetRouter(Agent router)
24-
{
25-
_router = router;
26-
}
27-
2822
public void SetDialogs(List<RoleDialogModel> dialogs)
2923
{
3024
_dialogs = dialogs;

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ namespace BotSharp.Core.Agents.Services;
44

55
public partial class AgentService
66
{
7+
#if !DEBUG
78
[MemoryCache(10 * 60)]
9+
#endif
810
public async Task<List<Agent>> GetAgents(bool? allowRouting = null)
911
{
1012
var agents = _db.GetAgents(allowRouting: allowRouting);

src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ public static IServiceCollection AddBotSharp(this IServiceCollection services, I
7171
services.AddSingleton((IServiceProvider x) => routingSettings);
7272

7373
services.AddScoped<NaivePlanner>();
74-
services.AddScoped<ReasoningPlanner>();
74+
services.AddScoped<HFPlanner>();
7575
services.AddScoped<IPlaner>(provider =>
7676
{
77-
if (routingSettings.Planner == nameof(ReasoningPlanner))
78-
return provider.GetRequiredService<ReasoningPlanner>();
77+
if (routingSettings.Planner == nameof(HFPlanner))
78+
return provider.GetRequiredService<HFPlanner>();
7979
else
8080
return provider.GetRequiredService<NaivePlanner>();
8181
});

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

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,18 @@ public async Task<bool> SendMessage(string agentId,
5353
var routing = _services.GetRequiredService<IRoutingService>();
5454
var settings = _services.GetRequiredService<RoutingSettings>();
5555

56-
var ret = agentId == settings.RouterId ?
56+
var response = agentId == settings.RouterId ?
5757
await routing.InstructLoop(message) :
5858
await routing.ExecuteOnce(agent, message);
5959

60-
await HandleAssistantMessage(message, onMessageReceived);
60+
await HandleAssistantMessage(response, onMessageReceived);
6161

6262
var statistics = _services.GetRequiredService<ITokenStatistics>();
6363
statistics.PrintStatistics();
6464

6565
routing.ResetRecursiveCounter();
66-
routing.RefreshDialogs();
6766

68-
return ret;
67+
return true;
6968
}
7069

7170
private async Task<Conversation> GetConversationRecord(string agentId)
@@ -86,15 +85,15 @@ private async Task<Conversation> GetConversationRecord(string agentId)
8685
return converation;
8786
}
8887

89-
private async Task HandleAssistantMessage(RoleDialogModel message, Func<RoleDialogModel, Task> onMessageReceived)
88+
private async Task HandleAssistantMessage(RoleDialogModel response, Func<RoleDialogModel, Task> onMessageReceived)
9089
{
9190
var agentService = _services.GetRequiredService<IAgentService>();
92-
var agent = await agentService.GetAgent(message.CurrentAgentId);
91+
var agent = await agentService.GetAgent(response.CurrentAgentId);
9392
var agentName = agent.Name;
9493

95-
var text = message.Role == AgentRole.Function ?
96-
$"Sending [{agentName}] {message.FunctionName}: {message.Content}" :
97-
$"Sending [{agentName}] {message.Role}: {message.Content}";
94+
var text = response.Role == AgentRole.Function ?
95+
$"Sending [{agentName}] {response.FunctionName}: {response.Content}" :
96+
$"Sending [{agentName}] {response.Role}: {response.Content}";
9897
#if DEBUG
9998
Console.WriteLine(text, Color.Yellow);
10099
#else
@@ -103,21 +102,21 @@ private async Task HandleAssistantMessage(RoleDialogModel message, Func<RoleDial
103102

104103
// Only read content from RichContent for UI rendering. When richContent is null, create a basic text message for richContent.
105104
var state = _services.GetRequiredService<IConversationStateService>();
106-
message.RichContent = message.RichContent ?? new RichContent<TextMessage>
105+
response.RichContent = response.RichContent ?? new RichContent<TextMessage>
107106
{
108107
Recipient = new Recipient { Id = state.GetConversationId() },
109-
Message = new TextMessage { Text = message.Content }
108+
Message = new TextMessage { Text = response.Content }
110109
};
111110

112111
var hooks = _services.GetServices<IConversationHook>().ToList();
113112
foreach (var hook in hooks)
114113
{
115-
await hook.OnResponseGenerated(message);
114+
await hook.OnResponseGenerated(response);
116115
}
117116

118-
await onMessageReceived(message);
117+
await onMessageReceived(response);
119118

120119
// Add to dialog history
121-
_storage.Append(_conversationId, message);
120+
_storage.Append(_conversationId, response);
122121
}
123122
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using BotSharp.Abstraction.Conversations.Models;
21
using BotSharp.Abstraction.Repositories;
32

43
namespace BotSharp.Core.Conversations.Services;
@@ -12,6 +11,7 @@ public partial class ConversationService : IConversationService
1211
private readonly IConversationStorage _storage;
1312
private readonly IConversationStateService _state;
1413
private string _conversationId;
14+
public string ConversationId => _conversationId;
1515

1616
public IConversationStateService States => _state;
1717

src/Infrastructure/BotSharp.Core/Evaluations/EvaluatingService.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ public async Task<Conversation> Execute(string task, EvaluationRequest request)
4343
};
4444

4545
var textCompletion = CompletionProvider.GetTextCompletion(_services);
46-
RoleDialogModel response = default;
46+
RoleDialogModel response = new RoleDialogModel(AgentRole.User, "");
4747
var dialogs = new List<RoleDialogModel>();
4848
int roundCount = 0;
4949
while (true)
5050
{
5151
// var text = string.Join("\r\n", dialogs.Select(x => $"{x.Role}: {x.Content}"));
5252
// text = instruction + $"\r\n###\r\n{text}\r\n{AgentRole.User}: ";
53-
var question = await textCompletion.GetCompletion(prompt);
53+
var question = await textCompletion.GetCompletion(prompt, request.AgentId, response.MessageId);
5454
dialogs.Add(new RoleDialogModel(AgentRole.User, question));
5555
prompt += question.Trim();
5656

@@ -61,9 +61,14 @@ public async Task<Conversation> Execute(string task, EvaluationRequest request)
6161

6262
roundCount++;
6363

64+
if (roundCount > 10)
65+
{
66+
Console.WriteLine($"Conversation ended due to execced max round count {roundCount}", Color.Red);
67+
break;
68+
}
69+
6470
if (response.FunctionName == "conversation_end" ||
65-
response.FunctionName == "human_intervention_needed" ||
66-
roundCount > 5)
71+
response.FunctionName == "human_intervention_needed")
6772
{
6873
Console.WriteLine($"Conversation ended by function {response.FunctionName}", Color.Green);
6974
break;

src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public async Task<InstructResult> Execute(string agentId, RoleDialogModel messag
4646
agentService.RenderedTemplate(agent, templateName);
4747

4848
var completer = CompletionProvider.GetTextCompletion(_services);
49-
var result = await completer.GetCompletion(prompt);
49+
var result = await completer.GetCompletion(prompt, agentId, message.MessageId);
5050
var response = new InstructResult
5151
{
5252
MessageId = message.MessageId,

src/Infrastructure/BotSharp.Core/Planning/ReasoningPlanner.cs renamed to src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,33 @@
33
using BotSharp.Abstraction.Planning;
44
using BotSharp.Abstraction.Repositories;
55
using BotSharp.Abstraction.Routing.Models;
6+
using BotSharp.Abstraction.Routing.Settings;
67
using BotSharp.Abstraction.Templating;
78

89
namespace BotSharp.Core.Planning;
910

10-
public class ReasoningPlanner : IPlaner
11+
/// <summary>
12+
/// Human feedback based planner
13+
/// </summary>
14+
public class HFPlanner : IPlaner
1115
{
1216
private readonly IServiceProvider _services;
1317
private readonly ILogger _logger;
1418

15-
public ReasoningPlanner(IServiceProvider services, ILogger<ReasoningPlanner> logger)
19+
public HFPlanner(IServiceProvider services, ILogger<HFPlanner> logger)
1620
{
1721
_services = services;
1822
_logger = logger;
1923
}
2024

21-
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
25+
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId)
2226
{
2327
var next = GetNextStepPrompt(router);
2428

2529
RoleDialogModel response = default;
2630
var inst = new FunctionCallFromLlm();
2731

28-
var completion = CompletionProvider.GetChatCompletion(_services,
29-
model: "llm-gpt4");
32+
var completion = CompletionProvider.GetChatCompletion(_services);
3033

3134
int retryCount = 0;
3235
while (retryCount < 3)
@@ -36,6 +39,9 @@ public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
3639
response = completion.GetChatCompletions(router, new List<RoleDialogModel>
3740
{
3841
new RoleDialogModel(AgentRole.User, next)
42+
{
43+
MessageId = messageId
44+
}
3945
});
4046

4147
inst = response.Content.JsonContent<FunctionCallFromLlm>();
@@ -59,14 +65,14 @@ public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
5965

6066
public async Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message)
6167
{
62-
message.Content = inst.Question;
63-
message.FunctionArgs = JsonSerializer.Serialize(inst.Arguments);
64-
65-
var db = _services.GetRequiredService<IBotSharpRepository>();
66-
var agent = db.GetAgents(inst.AgentName).FirstOrDefault();
68+
if (!string.IsNullOrEmpty(inst.AgentName))
69+
{
70+
var db = _services.GetRequiredService<IBotSharpRepository>();
71+
var agent = db.GetAgents(inst.AgentName).FirstOrDefault();
6772

68-
var context = _services.GetRequiredService<RoutingContext>();
69-
context.Push(agent.Id);
73+
var context = _services.GetRequiredService<RoutingContext>();
74+
context.Push(agent.Id);
75+
}
7076

7177
return true;
7278
}
@@ -76,9 +82,6 @@ public async Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel
7682
var context = _services.GetRequiredService<RoutingContext>();
7783
context.Pop();
7884

79-
// push Router to continue
80-
// Make decision according to last agent's response
81-
8285
return true;
8386
}
8487

0 commit comments

Comments
 (0)