HFPlanner
This commit is contained in:
parent
8bf61cb94c
commit
3caa7a5499
|
|
@ -47,4 +47,16 @@ More information about conversation hook please go to [Conversation Hook](../con
|
|||
```csharp
|
||||
Task OnStateLoaded(ConversationState state);
|
||||
Task OnStateChanged(string name, string preValue, string currentValue);
|
||||
```
|
||||
|
||||
### Content Generating Hook
|
||||
`IContentGeneratingHook`
|
||||
|
||||
Model content generating hook, it can be used for logging, metrics and tracing.
|
||||
```csharp
|
||||
// Before content generating.
|
||||
Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations);
|
||||
|
||||
// After content generated.
|
||||
Task AfterGenerated(RoleDialogModel message, TokenStatsModel tokenStats);
|
||||
```
|
||||
|
|
@ -3,6 +3,7 @@ namespace BotSharp.Abstraction.Conversations;
|
|||
public interface IConversationService
|
||||
{
|
||||
IConversationStateService States { get; }
|
||||
string ConversationId { get; }
|
||||
Task<Conversation> NewConversation(Conversation conversation);
|
||||
void SetConversationId(string conversationId, List<string> states);
|
||||
Task<Conversation> GetConversation(string id);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
public class RoleDialogModel : ITrackableMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// If Role is Assistant, it is same as user's message id.
|
||||
/// </summary>
|
||||
public string MessageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ namespace BotSharp.Abstraction.Conversations.Models;
|
|||
public class TokenStatsModel
|
||||
{
|
||||
public string Model { get; set; }
|
||||
public string Prompt { get; set; }
|
||||
public int PromptCount { get; set; }
|
||||
public int CompletionCount { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ public interface ITextCompletion
|
|||
/// <param name="model"></param>
|
||||
void SetModelName(string model);
|
||||
|
||||
Task<string> GetCompletion(string text);
|
||||
Task<string> GetCompletion(string text, string agentId, string messageId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ namespace BotSharp.Abstraction.Planning;
|
|||
|
||||
public interface IExecutor
|
||||
{
|
||||
Task<bool> Execute(IRoutingService routing,
|
||||
Agent router,
|
||||
Task<RoleDialogModel> Execute(IRoutingService routing,
|
||||
FunctionCallFromLlm inst,
|
||||
List<RoleDialogModel> dialogs,
|
||||
RoleDialogModel message);
|
||||
RoleDialogModel message,
|
||||
List<RoleDialogModel> dialogs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace BotSharp.Abstraction.Planning;
|
|||
/// </summary>
|
||||
public interface IPlaner
|
||||
{
|
||||
Task<FunctionCallFromLlm> GetNextInstruction(Agent router);
|
||||
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId);
|
||||
Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message);
|
||||
Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
|
||||
namespace BotSharp.Abstraction.Routing;
|
||||
|
||||
|
|
@ -15,9 +14,7 @@ public interface IRoutingHandler
|
|||
bool Enabled => true;
|
||||
List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>();
|
||||
|
||||
void SetRouter(Agent router) { }
|
||||
|
||||
void SetDialogs(List<RoleDialogModel> dialogs) { }
|
||||
void SetDialogs(List<RoleDialogModel> dialogs);
|
||||
|
||||
Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ namespace BotSharp.Abstraction.Routing;
|
|||
|
||||
public interface IRoutingService
|
||||
{
|
||||
List<RoleDialogModel> Dialogs { get; }
|
||||
Agent Router { get; }
|
||||
void ResetRecursiveCounter();
|
||||
void RefreshDialogs();
|
||||
Task<bool> InvokeAgent(string agentId, RoleDialogModel message);
|
||||
Task<bool> InstructLoop(RoleDialogModel message);
|
||||
Task<bool> ExecuteOnce(Agent agent, RoleDialogModel message);
|
||||
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);
|
||||
Task<RoleDialogModel> InstructLoop(RoleDialogModel message);
|
||||
Task<RoleDialogModel> ExecuteOnce(Agent agent, RoleDialogModel message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace BotSharp.Abstraction.Routing;
|
|||
|
||||
public abstract class RoutingHandlerBase
|
||||
{
|
||||
protected Agent _router;
|
||||
protected readonly IServiceProvider _services;
|
||||
protected readonly ILogger _logger;
|
||||
protected RoutingSettings _settings;
|
||||
|
|
@ -20,11 +19,6 @@ public abstract class RoutingHandlerBase
|
|||
_settings = settings;
|
||||
}
|
||||
|
||||
public void SetRouter(Agent router)
|
||||
{
|
||||
_router = router;
|
||||
}
|
||||
|
||||
public void SetDialogs(List<RoleDialogModel> dialogs)
|
||||
{
|
||||
_dialogs = dialogs;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ namespace BotSharp.Core.Agents.Services;
|
|||
|
||||
public partial class AgentService
|
||||
{
|
||||
#if !DEBUG
|
||||
[MemoryCache(10 * 60)]
|
||||
#endif
|
||||
public async Task<List<Agent>> GetAgents(bool? allowRouting = null)
|
||||
{
|
||||
var agents = _db.GetAgents(allowRouting: allowRouting);
|
||||
|
|
|
|||
|
|
@ -71,11 +71,11 @@ public static class BotSharpServiceCollectionExtensions
|
|||
services.AddSingleton((IServiceProvider x) => routingSettings);
|
||||
|
||||
services.AddScoped<NaivePlanner>();
|
||||
services.AddScoped<ReasoningPlanner>();
|
||||
services.AddScoped<HFPlanner>();
|
||||
services.AddScoped<IPlaner>(provider =>
|
||||
{
|
||||
if (routingSettings.Planner == nameof(ReasoningPlanner))
|
||||
return provider.GetRequiredService<ReasoningPlanner>();
|
||||
if (routingSettings.Planner == nameof(HFPlanner))
|
||||
return provider.GetRequiredService<HFPlanner>();
|
||||
else
|
||||
return provider.GetRequiredService<NaivePlanner>();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,19 +53,18 @@ public partial class ConversationService
|
|||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var settings = _services.GetRequiredService<RoutingSettings>();
|
||||
|
||||
var ret = agentId == settings.RouterId ?
|
||||
var response = agentId == settings.RouterId ?
|
||||
await routing.InstructLoop(message) :
|
||||
await routing.ExecuteOnce(agent, message);
|
||||
|
||||
await HandleAssistantMessage(message, onMessageReceived);
|
||||
await HandleAssistantMessage(response, onMessageReceived);
|
||||
|
||||
var statistics = _services.GetRequiredService<ITokenStatistics>();
|
||||
statistics.PrintStatistics();
|
||||
|
||||
routing.ResetRecursiveCounter();
|
||||
routing.RefreshDialogs();
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<Conversation> GetConversationRecord(string agentId)
|
||||
|
|
@ -86,15 +85,15 @@ public partial class ConversationService
|
|||
return converation;
|
||||
}
|
||||
|
||||
private async Task HandleAssistantMessage(RoleDialogModel message, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
private async Task HandleAssistantMessage(RoleDialogModel response, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.GetAgent(message.CurrentAgentId);
|
||||
var agent = await agentService.GetAgent(response.CurrentAgentId);
|
||||
var agentName = agent.Name;
|
||||
|
||||
var text = message.Role == AgentRole.Function ?
|
||||
$"Sending [{agentName}] {message.FunctionName}: {message.Content}" :
|
||||
$"Sending [{agentName}] {message.Role}: {message.Content}";
|
||||
var text = response.Role == AgentRole.Function ?
|
||||
$"Sending [{agentName}] {response.FunctionName}: {response.Content}" :
|
||||
$"Sending [{agentName}] {response.Role}: {response.Content}";
|
||||
#if DEBUG
|
||||
Console.WriteLine(text, Color.Yellow);
|
||||
#else
|
||||
|
|
@ -103,21 +102,21 @@ public partial class ConversationService
|
|||
|
||||
// Only read content from RichContent for UI rendering. When richContent is null, create a basic text message for richContent.
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
message.RichContent = message.RichContent ?? new RichContent<TextMessage>
|
||||
response.RichContent = response.RichContent ?? new RichContent<TextMessage>
|
||||
{
|
||||
Recipient = new Recipient { Id = state.GetConversationId() },
|
||||
Message = new TextMessage { Text = message.Content }
|
||||
Message = new TextMessage { Text = response.Content }
|
||||
};
|
||||
|
||||
var hooks = _services.GetServices<IConversationHook>().ToList();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.OnResponseGenerated(message);
|
||||
await hook.OnResponseGenerated(response);
|
||||
}
|
||||
|
||||
await onMessageReceived(message);
|
||||
await onMessageReceived(response);
|
||||
|
||||
// Add to dialog history
|
||||
_storage.Append(_conversationId, message);
|
||||
_storage.Append(_conversationId, response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
|
||||
namespace BotSharp.Core.Conversations.Services;
|
||||
|
|
@ -12,6 +11,7 @@ public partial class ConversationService : IConversationService
|
|||
private readonly IConversationStorage _storage;
|
||||
private readonly IConversationStateService _state;
|
||||
private string _conversationId;
|
||||
public string ConversationId => _conversationId;
|
||||
|
||||
public IConversationStateService States => _state;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ public class EvaluatingService : IEvaluatingService
|
|||
};
|
||||
|
||||
var textCompletion = CompletionProvider.GetTextCompletion(_services);
|
||||
RoleDialogModel response = default;
|
||||
RoleDialogModel response = new RoleDialogModel(AgentRole.User, "");
|
||||
var dialogs = new List<RoleDialogModel>();
|
||||
int roundCount = 0;
|
||||
while (true)
|
||||
{
|
||||
// var text = string.Join("\r\n", dialogs.Select(x => $"{x.Role}: {x.Content}"));
|
||||
// text = instruction + $"\r\n###\r\n{text}\r\n{AgentRole.User}: ";
|
||||
var question = await textCompletion.GetCompletion(prompt);
|
||||
var question = await textCompletion.GetCompletion(prompt, request.AgentId, response.MessageId);
|
||||
dialogs.Add(new RoleDialogModel(AgentRole.User, question));
|
||||
prompt += question.Trim();
|
||||
|
||||
|
|
@ -61,9 +61,14 @@ public class EvaluatingService : IEvaluatingService
|
|||
|
||||
roundCount++;
|
||||
|
||||
if (roundCount > 10)
|
||||
{
|
||||
Console.WriteLine($"Conversation ended due to execced max round count {roundCount}", Color.Red);
|
||||
break;
|
||||
}
|
||||
|
||||
if (response.FunctionName == "conversation_end" ||
|
||||
response.FunctionName == "human_intervention_needed" ||
|
||||
roundCount > 5)
|
||||
response.FunctionName == "human_intervention_needed")
|
||||
{
|
||||
Console.WriteLine($"Conversation ended by function {response.FunctionName}", Color.Green);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public partial class InstructService : IInstructService
|
|||
agentService.RenderedTemplate(agent, templateName);
|
||||
|
||||
var completer = CompletionProvider.GetTextCompletion(_services);
|
||||
var result = await completer.GetCompletion(prompt);
|
||||
var result = await completer.GetCompletion(prompt, agentId, message.MessageId);
|
||||
var response = new InstructResult
|
||||
{
|
||||
MessageId = message.MessageId,
|
||||
|
|
|
|||
|
|
@ -3,30 +3,33 @@ using BotSharp.Abstraction.Functions.Models;
|
|||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
namespace BotSharp.Core.Planning;
|
||||
|
||||
public class ReasoningPlanner : IPlaner
|
||||
/// <summary>
|
||||
/// Human feedback based planner
|
||||
/// </summary>
|
||||
public class HFPlanner : IPlaner
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ReasoningPlanner(IServiceProvider services, ILogger<ReasoningPlanner> logger)
|
||||
public HFPlanner(IServiceProvider services, ILogger<HFPlanner> logger)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
|
||||
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId)
|
||||
{
|
||||
var next = GetNextStepPrompt(router);
|
||||
|
||||
RoleDialogModel response = default;
|
||||
var inst = new FunctionCallFromLlm();
|
||||
|
||||
var completion = CompletionProvider.GetChatCompletion(_services,
|
||||
model: "llm-gpt4");
|
||||
var completion = CompletionProvider.GetChatCompletion(_services);
|
||||
|
||||
int retryCount = 0;
|
||||
while (retryCount < 3)
|
||||
|
|
@ -36,6 +39,9 @@ public class ReasoningPlanner : IPlaner
|
|||
response = completion.GetChatCompletions(router, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, next)
|
||||
{
|
||||
MessageId = messageId
|
||||
}
|
||||
});
|
||||
|
||||
inst = response.Content.JsonContent<FunctionCallFromLlm>();
|
||||
|
|
@ -59,14 +65,14 @@ public class ReasoningPlanner : IPlaner
|
|||
|
||||
public async Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message)
|
||||
{
|
||||
message.Content = inst.Question;
|
||||
message.FunctionArgs = JsonSerializer.Serialize(inst.Arguments);
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var agent = db.GetAgents(inst.AgentName).FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(inst.AgentName))
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var agent = db.GetAgents(inst.AgentName).FirstOrDefault();
|
||||
|
||||
var context = _services.GetRequiredService<RoutingContext>();
|
||||
context.Push(agent.Id);
|
||||
var context = _services.GetRequiredService<RoutingContext>();
|
||||
context.Push(agent.Id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -76,9 +82,6 @@ public class ReasoningPlanner : IPlaner
|
|||
var context = _services.GetRequiredService<RoutingContext>();
|
||||
context.Pop();
|
||||
|
||||
// push Router to continue
|
||||
// Make decision according to last agent's response
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
|
|
@ -16,30 +15,24 @@ public class InstructExecutor : IExecutor
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<bool> Execute(IRoutingService routing,
|
||||
Agent router,
|
||||
public async Task<RoleDialogModel> Execute(IRoutingService routing,
|
||||
FunctionCallFromLlm inst,
|
||||
List<RoleDialogModel> dialogs,
|
||||
RoleDialogModel message)
|
||||
RoleDialogModel message,
|
||||
List<RoleDialogModel> dialogs)
|
||||
{
|
||||
// Set user content as Planner's question
|
||||
inst.Question = message.Content;
|
||||
message.Instruction = inst;
|
||||
|
||||
var handlers = _services.GetServices<IRoutingHandler>();
|
||||
|
||||
var handler = handlers.FirstOrDefault(x => x.Name == inst.Function);
|
||||
handler.SetRouter(router);
|
||||
handler.SetDialogs(dialogs);
|
||||
|
||||
message.FunctionName = inst.Function;
|
||||
message.Role = AgentRole.Function;
|
||||
message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments);
|
||||
|
||||
var handled = await handler.Handle(routing, inst, message);
|
||||
|
||||
inst.Response = message.Content;
|
||||
// For client display purpose
|
||||
var response = dialogs.Last();
|
||||
response.MessageId = message.MessageId;
|
||||
response.Instruction = inst;
|
||||
|
||||
return handled;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using BotSharp.Abstraction.Functions.Models;
|
|||
using BotSharp.Abstraction.Planning;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
|
||||
namespace BotSharp.Core.Planning;
|
||||
|
||||
|
|
@ -17,11 +18,10 @@ public class NaivePlanner : IPlaner
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
|
||||
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId)
|
||||
{
|
||||
var next = GetNextStepPrompt(router);
|
||||
|
||||
RoleDialogModel response = default;
|
||||
var inst = new FunctionCallFromLlm();
|
||||
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
|
|
@ -36,16 +36,20 @@ public class NaivePlanner : IPlaner
|
|||
int retryCount = 0;
|
||||
while (retryCount < 3)
|
||||
{
|
||||
string text = string.Empty;
|
||||
try
|
||||
{
|
||||
var text = await completion.GetCompletion(content);
|
||||
response = new RoleDialogModel(AgentRole.Assistant, text);
|
||||
text = await completion.GetCompletion(content, router.Id, messageId);
|
||||
var response = new RoleDialogModel(AgentRole.Assistant, text)
|
||||
{
|
||||
MessageId = messageId
|
||||
};
|
||||
inst = response.Content.JsonContent<FunctionCallFromLlm>();
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"{ex.Message}: {response.Content}");
|
||||
_logger.LogError($"{ex.Message}: {text}");
|
||||
inst.Function = "response_to_user";
|
||||
inst.Response = ex.Message;
|
||||
inst.AgentName = "Router";
|
||||
|
|
@ -64,6 +68,10 @@ public class NaivePlanner : IPlaner
|
|||
|
||||
public async Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message)
|
||||
{
|
||||
// Set user content as Planner's question
|
||||
message.FunctionName = inst.Function;
|
||||
message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using BotSharp.Core.Planning;
|
|||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
||||
public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase//, IRoutingHandler
|
||||
{
|
||||
public string Name => "continue_execute_task";
|
||||
|
||||
|
|
@ -15,7 +15,8 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan
|
|||
|
||||
public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
|
||||
{
|
||||
new ParameterPropertyDef("agent", "the name of the agent"),
|
||||
new ParameterPropertyDef("next_action_agent", "agent for next action based on user latest response"),
|
||||
new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal"),
|
||||
new ParameterPropertyDef("reason", "why continue to execute current task"),
|
||||
new ParameterPropertyDef("args", "required parameters extracted from question")
|
||||
{
|
||||
|
|
@ -25,7 +26,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan
|
|||
|
||||
public List<string> Planers => new List<string>
|
||||
{
|
||||
nameof(ReasoningPlanner)
|
||||
nameof(HFPlanner)
|
||||
};
|
||||
|
||||
public ContinueExecuteTaskRoutingHandler(IServiceProvider services, ILogger<ContinueExecuteTaskRoutingHandler> logger, RoutingSettings settings)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
|
||||
|
|
@ -25,8 +24,15 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
|
||||
{
|
||||
message.Content = inst.Response;
|
||||
message.FunctionName = inst.Function;
|
||||
var response = new RoleDialogModel(AgentRole.Assistant, inst.Response)
|
||||
{
|
||||
CurrentAgentId = message.CurrentAgentId,
|
||||
MessageId = message.MessageId,
|
||||
StopCompletion = true,
|
||||
FunctionName = inst.Function
|
||||
};
|
||||
|
||||
_dialogs.Add(response);
|
||||
|
||||
var hooks = _services.GetServices<IConversationHook>()
|
||||
.OrderBy(x => x.Priority)
|
||||
|
|
@ -34,7 +40,7 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.OnConversationEnding(message);
|
||||
await hook.OnConversationEnding(response);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -24,8 +24,15 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle
|
|||
|
||||
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
|
||||
{
|
||||
message.Role = AgentRole.Assistant;
|
||||
message.Content = inst.Response;
|
||||
var response = new RoleDialogModel(AgentRole.Assistant, inst.Response)
|
||||
{
|
||||
CurrentAgentId = message.CurrentAgentId,
|
||||
MessageId = message.MessageId,
|
||||
StopCompletion = true,
|
||||
FunctionName = inst.Function
|
||||
};
|
||||
|
||||
_dialogs.Add(response);
|
||||
|
||||
var hooks = _services.GetServices<IConversationHook>()
|
||||
.OrderBy(x => x.Priority)
|
||||
|
|
@ -33,7 +40,7 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle
|
|||
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.OnHumanInterventionNeeded(message);
|
||||
await hook.OnHumanInterventionNeeded(response);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using BotSharp.Core.Planning;
|
|||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
||||
public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase//, IRoutingHandler
|
||||
{
|
||||
public string Name => "interrupt_task_execution";
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRouting
|
|||
|
||||
public List<string> Planers => new List<string>
|
||||
{
|
||||
nameof(ReasoningPlanner)
|
||||
nameof(HFPlanner)
|
||||
};
|
||||
|
||||
public InterruptTaskExecutionRoutingHandler(IServiceProvider services, ILogger<InterruptTaskExecutionRoutingHandler> logger, RoutingSettings settings)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
|
||||
|
|
@ -24,9 +23,15 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
|
||||
{
|
||||
message.Content = inst.Response;
|
||||
message.StopCompletion = true;
|
||||
message.Role = AgentRole.Assistant;
|
||||
var response = new RoleDialogModel(AgentRole.Assistant, inst.Response)
|
||||
{
|
||||
CurrentAgentId = message.CurrentAgentId,
|
||||
MessageId = message.MessageId,
|
||||
StopCompletion = true
|
||||
};
|
||||
|
||||
_dialogs.Add(response);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
|
|
@ -21,6 +20,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
|
|||
new ParameterPropertyDef("reason", "why retrieve data"),
|
||||
new ParameterPropertyDef("question", "the question you will ask the agent to get the necessary data"),
|
||||
new ParameterPropertyDef("next_action_agent", "agent that can handle the question"),
|
||||
|
||||
new ParameterPropertyDef("args", "required parameters extracted from question and hand over to the next agent")
|
||||
{
|
||||
Type = "object"
|
||||
|
|
@ -29,7 +29,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
|
|||
|
||||
public List<string> Planers => new List<string>
|
||||
{
|
||||
nameof(ReasoningPlanner)
|
||||
nameof(HFPlanner)
|
||||
};
|
||||
|
||||
public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger<RetrieveDataFromAgentRoutingHandler> logger, RoutingSettings settings)
|
||||
|
|
@ -40,7 +40,22 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
|
|||
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
|
||||
{
|
||||
var context = _services.GetRequiredService<RoutingContext>();
|
||||
var ret = await routing.InvokeAgent(context.GetCurrentAgentId(), message);
|
||||
var agentId = context.GetCurrentAgentId();
|
||||
var dialogs = new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, inst.Question)
|
||||
{
|
||||
CurrentAgentId = agentId,
|
||||
MessageId = message.MessageId
|
||||
}
|
||||
};
|
||||
|
||||
var ret = await routing.InvokeAgent(agentId, dialogs);
|
||||
var response = dialogs.Last();
|
||||
inst.Response = response.Content;
|
||||
|
||||
// Add final response to parent dialog
|
||||
_dialogs.Add(response);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,11 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
message.FunctionArgs = JsonSerializer.Serialize(inst);
|
||||
var ret = await function.Execute(message);
|
||||
|
||||
ret = await routing.InvokeAgent(context.GetCurrentAgentId(), message);
|
||||
var agentId = context.GetCurrentAgentId();
|
||||
ret = await routing.InvokeAgent(agentId, _dialogs);
|
||||
|
||||
var response = _dialogs.Last();
|
||||
inst.Response = response.Content;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public List<string> Planers => new List<string>
|
||||
{
|
||||
nameof(ReasoningPlanner)
|
||||
nameof(HFPlanner)
|
||||
};
|
||||
|
||||
public TaskEndRoutingHandler(IServiceProvider services, ILogger<TaskEndRoutingHandler> logger, RoutingSettings settings)
|
||||
|
|
@ -32,10 +32,9 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
.OrderBy(x => x.Priority)
|
||||
.ToList();
|
||||
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.OnCurrentTaskEnding(message);
|
||||
}
|
||||
Task.WaitAll(hooks
|
||||
.Select(h => h.OnCurrentTaskEnding(message))
|
||||
.ToArray());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public partial class RoutingService
|
|||
{
|
||||
const int MAXIMUM_RECURSION_DEPTH = 3;
|
||||
private int _currentRecursionDepth = 0;
|
||||
public async Task<bool> InvokeAgent(string agentId, RoleDialogModel message)
|
||||
public async Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs)
|
||||
{
|
||||
_currentRecursionDepth++;
|
||||
if (_currentRecursionDepth > MAXIMUM_RECURSION_DEPTH)
|
||||
|
|
@ -22,25 +22,22 @@ public partial class RoutingService
|
|||
|
||||
var settings = _services.GetRequiredService<ChatCompletionSetting>();
|
||||
var chatCompletion = CompletionProvider.GetChatCompletion(_services, provider: settings.Provider, model: settings.Model);
|
||||
RoleDialogModel response = chatCompletion.GetChatCompletions(agent, Dialogs);
|
||||
message.Role = response.Role;
|
||||
|
||||
RoleDialogModel response = chatCompletion.GetChatCompletions(agent, dialogs);
|
||||
|
||||
if (response.Role == AgentRole.Function)
|
||||
{
|
||||
message.FunctionName = response.FunctionName;
|
||||
message.FunctionArgs = response.FunctionArgs;
|
||||
|
||||
await InvokeFunction(agent, message);
|
||||
await InvokeFunction(agent, response, dialogs);
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Content = response.Content;
|
||||
dialogs.Add(response);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<RoleDialogModel> InvokeFunction(Agent agent, RoleDialogModel message)
|
||||
private async Task<bool> InvokeFunction(Agent agent, RoleDialogModel message, List<RoleDialogModel> dialogs)
|
||||
{
|
||||
// execute function
|
||||
// Save states
|
||||
|
|
@ -50,8 +47,6 @@ public partial class RoutingService
|
|||
// Call functions
|
||||
await conversationService.CallFunctions(message);
|
||||
|
||||
Dialogs.Add(message);
|
||||
|
||||
// Pass execution result to LLM to get response
|
||||
if (!message.StopCompletion)
|
||||
{
|
||||
|
|
@ -60,19 +55,24 @@ public partial class RoutingService
|
|||
var responseTemplate = await templateService.RenderFunctionResponse(agent.Id, message);
|
||||
if (!string.IsNullOrEmpty(responseTemplate))
|
||||
{
|
||||
message.Role = AgentRole.Assistant;
|
||||
message.Content = responseTemplate.Trim();
|
||||
message.Role = AgentRole.Assistant;
|
||||
dialogs.Add(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeAgent(agent.Id, message);
|
||||
// Save to memory dialogs
|
||||
dialogs.Add(new RoleDialogModel(AgentRole.Function, message.Content)
|
||||
{
|
||||
FunctionArgs = message.FunctionArgs,
|
||||
FunctionName = message.FunctionName
|
||||
});
|
||||
|
||||
// Send to LLM
|
||||
await InvokeAgent(agent.Id, dialogs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Role = AgentRole.Assistant;
|
||||
}
|
||||
|
||||
return message;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,30 +14,14 @@ public partial class RoutingService : IRoutingService
|
|||
private readonly RoutingSettings _settings;
|
||||
private readonly IRouterInstance _routerInstance;
|
||||
private readonly ILogger _logger;
|
||||
private List<RoleDialogModel> _dialogs;
|
||||
public List<RoleDialogModel> Dialogs {
|
||||
get
|
||||
{
|
||||
if (_dialogs == null)
|
||||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
_dialogs = conv.GetDialogHistory();
|
||||
}
|
||||
|
||||
return _dialogs;
|
||||
}
|
||||
}
|
||||
private Agent _router;
|
||||
public Agent Router => _router;
|
||||
|
||||
public void ResetRecursiveCounter()
|
||||
{
|
||||
_currentRecursionDepth = 0;
|
||||
}
|
||||
|
||||
public void RefreshDialogs()
|
||||
{
|
||||
_dialogs = null;
|
||||
}
|
||||
|
||||
public RoutingService(IServiceProvider services,
|
||||
RoutingSettings settings,
|
||||
ILogger<RoutingService> logger,
|
||||
|
|
@ -49,44 +33,57 @@ public partial class RoutingService : IRoutingService
|
|||
_routerInstance = routerInstance;
|
||||
}
|
||||
|
||||
public async Task<bool> ExecuteOnce(Agent agent, RoleDialogModel message)
|
||||
public async Task<RoleDialogModel> ExecuteOnce(Agent agent, RoleDialogModel message)
|
||||
{
|
||||
var handlers = _services.GetServices<IRoutingHandler>();
|
||||
|
||||
var handler = handlers.FirstOrDefault(x => x.Name == "route_to_agent");
|
||||
handler.SetDialogs(Dialogs);
|
||||
var dialogs = new List<RoleDialogModel> { message };
|
||||
handler.SetDialogs(dialogs);
|
||||
|
||||
var result = await handler.Handle(this, new FunctionCallFromLlm
|
||||
var inst = new FunctionCallFromLlm
|
||||
{
|
||||
Function = "route_to_agent",
|
||||
Question = message.Content,
|
||||
Reason = message.Content,
|
||||
AgentName = agent.Name
|
||||
}, message);
|
||||
};
|
||||
|
||||
return result;
|
||||
var result = await handler.Handle(this, inst, message);
|
||||
|
||||
var response = dialogs.Last();
|
||||
response.MessageId = message.MessageId;
|
||||
response.Instruction = inst;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<bool> InstructLoop(RoleDialogModel message)
|
||||
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message)
|
||||
{
|
||||
_routerInstance.Load();
|
||||
var router = _routerInstance.Router;
|
||||
_router = _routerInstance.Load()
|
||||
.Router;
|
||||
|
||||
RoleDialogModel response = default;
|
||||
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var dialogs = conv.GetDialogHistory();
|
||||
|
||||
var context = _services.GetRequiredService<RoutingContext>();
|
||||
var planner = _services.GetRequiredService<IPlaner>();
|
||||
var executor = _services.GetRequiredService<IExecutor>();
|
||||
|
||||
context.Push(_router.Id);
|
||||
|
||||
int loopCount = 0;
|
||||
var stop = false;
|
||||
while (!stop && loopCount < 5)
|
||||
while (loopCount < 5 && !context.IsEmpty)
|
||||
{
|
||||
loopCount++;
|
||||
|
||||
var conversation = await GetConversationContent(Dialogs);
|
||||
router.TemplateDict["conversation"] = conversation;
|
||||
var conversation = await GetConversationContent(dialogs);
|
||||
_router.TemplateDict["conversation"] = conversation;
|
||||
|
||||
// Get instruction from Planner
|
||||
var inst = await planner.GetNextInstruction(router);
|
||||
var inst = await planner.GetNextInstruction(_router, message.MessageId);
|
||||
|
||||
// Save states
|
||||
SaveStateByArgs(inst.Arguments);
|
||||
|
|
@ -99,18 +96,12 @@ public partial class RoutingService : IRoutingService
|
|||
await planner.AgentExecuting(inst, message);
|
||||
|
||||
// Handle instruction by Executor
|
||||
var executed = await executor.Execute(this, router, inst, Dialogs, message);
|
||||
response = await executor.Execute(this, inst, message, dialogs);
|
||||
|
||||
await planner.AgentExecuted(inst, message);
|
||||
|
||||
// There is no need for the agent to continue processing, indicating that the task has been completed.
|
||||
if (context.IsEmpty || context.GetCurrentAgentId() == router.Id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await planner.AgentExecuted(inst, response);
|
||||
}
|
||||
|
||||
return true;
|
||||
return response;
|
||||
}
|
||||
|
||||
protected void SaveStateByArgs(JsonDocument args)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,11 @@ public class ConversationController : ControllerBase, IApiAdapter
|
|||
await conv.SendMessage(agentId, inputMsg,
|
||||
async msg =>
|
||||
{
|
||||
|
||||
response.Text = msg.Content;
|
||||
response.Function = msg.FunctionName;
|
||||
response.RichContent = msg.RichContent;
|
||||
response.Instruction = msg.Instruction;
|
||||
response.Data = msg.Data;
|
||||
},
|
||||
async fnExecuting =>
|
||||
{
|
||||
|
|
@ -69,11 +73,6 @@ public class ConversationController : ControllerBase, IApiAdapter
|
|||
});
|
||||
|
||||
response.MessageId = inputMsg.MessageId;
|
||||
response.Text = inputMsg.Content;
|
||||
response.Data = inputMsg.Data;
|
||||
response.Function = inputMsg.FunctionName;
|
||||
response.Instruction = inputMsg.Instruction;
|
||||
response.RichContent = inputMsg.RichContent;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.ApiAdapters;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Instructs;
|
||||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using BotSharp.Core.Infrastructures;
|
||||
using BotSharp.OpenAPI.ViewModels.Instructs;
|
||||
|
||||
|
|
@ -50,6 +48,6 @@ public class InstructModeController : ControllerBase, IApiAdapter
|
|||
.SetState("model", input.Model);
|
||||
|
||||
var textCompletion = CompletionProvider.GetTextCompletion(_services);
|
||||
return await textCompletion.GetCompletion(input.Text);
|
||||
return await textCompletion.GetCompletion(input.Text, Guid.Empty.ToString(), Guid.Empty.ToString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Conversations;
|
||||
|
||||
|
|
@ -8,7 +9,10 @@ public class MessageResponseModel : ITrackableMessage
|
|||
public string MessageId { get; set; }
|
||||
public string Text { get; set; }
|
||||
public string Function { get; set; }
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public object Data { get; set; }
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public FunctionCallFromLlm Instruction { get; set; }
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public object? RichContent { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using BotSharp.Abstraction.Conversations.Models;
|
|||
using BotSharp.Abstraction.Conversations.Settings;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Plugin.AzureOpenAI.Settings;
|
||||
using Microsoft.Extensions.Azure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
|
|
@ -45,51 +46,54 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
hook.BeforeGenerating(agent, conversations)).ToArray());
|
||||
|
||||
var client = ProviderHelper.GetClient(_model, _settings);
|
||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||
var (prompt, chatCompletionsOptions) = PrepareOptions(agent, conversations);
|
||||
|
||||
var response = client.GetChatCompletions(_model, chatCompletionsOptions);
|
||||
var choice = response.Value.Choices[0];
|
||||
var message = choice.Message;
|
||||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, message.Content)
|
||||
var responseMessage = new RoleDialogModel(AgentRole.Assistant, message.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.Last().MessageId
|
||||
};
|
||||
|
||||
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
|
||||
{
|
||||
msg = new RoleDialogModel(AgentRole.Function, message.Content)
|
||||
responseMessage = new RoleDialogModel(AgentRole.Function, message.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.Last().MessageId,
|
||||
FunctionName = message.FunctionCall.Name,
|
||||
FunctionArgs = message.FunctionCall.Arguments
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
if (!string.IsNullOrEmpty(msg.FunctionName))
|
||||
if (!string.IsNullOrEmpty(responseMessage.FunctionName))
|
||||
{
|
||||
msg.FunctionName = msg.FunctionName.Split('.').Last();
|
||||
responseMessage.FunctionName = responseMessage.FunctionName.Split('.').Last();
|
||||
}
|
||||
}
|
||||
|
||||
var setting = _services.GetRequiredService<ConversationSetting>();
|
||||
if (setting.ShowVerboseLog)
|
||||
{
|
||||
_logger.LogInformation(msg.Role == AgentRole.Function ?
|
||||
$"[{agent.Name}]: {msg.FunctionName}({msg.FunctionArgs})" :
|
||||
$"[{agent.Name}]: {msg.Content}");
|
||||
_logger.LogInformation(responseMessage.Role == AgentRole.Function ?
|
||||
$"[{agent.Name}]: {responseMessage.FunctionName}({responseMessage.FunctionArgs})" :
|
||||
$"[{agent.Name}]: {responseMessage.Content}");
|
||||
}
|
||||
|
||||
// After chat completion hook
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.AfterGenerated(msg, new TokenStatsModel
|
||||
hook.AfterGenerated(responseMessage, new TokenStatsModel
|
||||
{
|
||||
Prompt = prompt,
|
||||
Model = _model,
|
||||
PromptCount = response.Value.Usage.PromptTokens,
|
||||
CompletionCount = response.Value.Usage.CompletionTokens
|
||||
})).ToArray());
|
||||
|
||||
return msg;
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
public async Task<bool> GetChatCompletionsAsync(Agent agent,
|
||||
|
|
@ -104,7 +108,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
hook.BeforeGenerating(agent, conversations)).ToArray());
|
||||
|
||||
var client = ProviderHelper.GetClient(_model, _settings);
|
||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||
var (prompt, chatCompletionsOptions) = PrepareOptions(agent, conversations);
|
||||
|
||||
var response = await client.GetChatCompletionsAsync(_model, chatCompletionsOptions);
|
||||
var choice = response.Value.Choices[0];
|
||||
|
|
@ -119,6 +123,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.AfterGenerated(msg, new TokenStatsModel
|
||||
{
|
||||
Prompt = prompt,
|
||||
Model = _model,
|
||||
PromptCount = response.Value.Usage.PromptTokens,
|
||||
CompletionCount = response.Value.Usage.CompletionTokens
|
||||
|
|
@ -156,7 +161,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
public async Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var client = ProviderHelper.GetClient(_model, _settings);
|
||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||
var (prompt, chatCompletionsOptions) = PrepareOptions(agent, conversations);
|
||||
|
||||
var response = await client.GetChatCompletionsStreamingAsync(_model, chatCompletionsOptions);
|
||||
using StreamingChatCompletions streaming = response.Value;
|
||||
|
|
@ -198,7 +203,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
}
|
||||
|
||||
|
||||
protected ChatCompletionsOptions PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
||||
protected (string, ChatCompletionsOptions) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
|
||||
|
|
@ -255,33 +260,50 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
// chatCompletionsOptions.FrequencyPenalty = 0;
|
||||
// chatCompletionsOptions.PresencePenalty = 0;
|
||||
|
||||
var prompt = GetPrompt(chatCompletionsOptions);
|
||||
var convSetting = _services.GetRequiredService<ConversationSetting>();
|
||||
if (convSetting.ShowVerboseLog)
|
||||
{
|
||||
if (chatCompletionsOptions.Messages.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("VERBOSE COMPLETION MESSAGES");
|
||||
var verbose = string.Join("\r\n", chatCompletionsOptions.Messages.Select(x =>
|
||||
_logger.LogInformation(prompt);
|
||||
}
|
||||
|
||||
return (prompt, chatCompletionsOptions);
|
||||
}
|
||||
|
||||
private string GetPrompt(ChatCompletionsOptions chatCompletionsOptions)
|
||||
{
|
||||
var prompt = string.Empty;
|
||||
|
||||
if (chatCompletionsOptions.Messages.Count > 0)
|
||||
{
|
||||
// System instruction
|
||||
var verbose = string.Join("\r\n", chatCompletionsOptions.Messages
|
||||
.Where(x => x.Role == AgentRole.System).Select(x =>
|
||||
{
|
||||
return $"{x.Role}: {x.Content}";
|
||||
}));
|
||||
prompt += $"\r\n[INSTRUCTION]\r\n{verbose}\r\n";
|
||||
|
||||
verbose = string.Join("\r\n", chatCompletionsOptions.Messages
|
||||
.Where(x => x.Role != AgentRole.System).Select(x =>
|
||||
{
|
||||
return x.Role == ChatRole.Function ?
|
||||
$"{x.Role}: {x.Name} => {x.Content}" :
|
||||
$"{x.Role}: {x.Content}";
|
||||
}));
|
||||
_logger.LogInformation(verbose);
|
||||
}
|
||||
|
||||
if (chatCompletionsOptions.Functions.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("VERBOSE FUNCTIONS");
|
||||
var verbose = string.Join("\r\n", chatCompletionsOptions.Functions.Select(x =>
|
||||
{
|
||||
return $"{x.Name}: {x.Description}\r\n{x.Parameters}";
|
||||
}));
|
||||
_logger.LogInformation(verbose);
|
||||
}
|
||||
prompt += $"\r\n[CONVERSATION]\r\n{verbose}\r\n";
|
||||
}
|
||||
|
||||
return chatCompletionsOptions;
|
||||
if (chatCompletionsOptions.Functions.Count > 0)
|
||||
{
|
||||
var functions = string.Join("\r\n", chatCompletionsOptions.Functions.Select(x =>
|
||||
{
|
||||
return $"{x.Name}: {x.Description}\r\n{x.Parameters}";
|
||||
}));
|
||||
prompt += $"\r\n[FUNCTIONS]\r\n{functions}\r\n";
|
||||
}
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
public void SetModelName(string model)
|
||||
|
|
|
|||
|
|
@ -32,16 +32,26 @@ public class TextCompletionProvider : ITextCompletion
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<string> GetCompletion(string text)
|
||||
public async Task<string> GetCompletion(string text, string agentId, string messageId)
|
||||
{
|
||||
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
|
||||
|
||||
// Before chat completion hook
|
||||
var agent = new Agent()
|
||||
{
|
||||
Id = agentId,
|
||||
};
|
||||
var message = new RoleDialogModel(AgentRole.User, text)
|
||||
{
|
||||
CurrentAgentId = agentId,
|
||||
MessageId = messageId
|
||||
};
|
||||
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.BeforeGenerating(new Agent(),
|
||||
new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, text)
|
||||
hook.BeforeGenerating(agent,
|
||||
new List<RoleDialogModel>
|
||||
{
|
||||
message
|
||||
})).ToArray());
|
||||
|
||||
var client = ProviderHelper.GetClient(_model, _settings);
|
||||
|
|
@ -85,9 +95,15 @@ public class TextCompletionProvider : ITextCompletion
|
|||
}
|
||||
|
||||
// After chat completion hook
|
||||
var responseMessage = new RoleDialogModel(AgentRole.Assistant, completion)
|
||||
{
|
||||
CurrentAgentId = agentId,
|
||||
MessageId = messageId
|
||||
};
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, completion), new TokenStatsModel
|
||||
hook.AfterGenerated(responseMessage, new TokenStatsModel
|
||||
{
|
||||
Prompt = text,
|
||||
Model = _model,
|
||||
PromptCount = response.Value.Usage.PromptTokens,
|
||||
CompletionCount = response.Value.Usage.CompletionTokens
|
||||
|
|
|
|||
|
|
@ -26,13 +26,21 @@ public class TextCompletionProvider : ITextCompletion
|
|||
_tokenStatistics = tokenStatistics;
|
||||
}
|
||||
|
||||
public async Task<string> GetCompletion(string text)
|
||||
public async Task<string> GetCompletion(string text, string agentId, string messageId)
|
||||
{
|
||||
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
|
||||
|
||||
// Before chat completion hook
|
||||
var agent = new Agent()
|
||||
{
|
||||
Id = agentId
|
||||
};
|
||||
var userMessage = new RoleDialogModel(AgentRole.User, text)
|
||||
{
|
||||
MessageId = messageId
|
||||
};
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.BeforeGenerating(new Agent(), new List<RoleDialogModel> { new RoleDialogModel(AgentRole.User, text) })).ToArray());
|
||||
hook.BeforeGenerating(agent, new List<RoleDialogModel> { userMessage })).ToArray());
|
||||
|
||||
var client = new GooglePalmClient(apiKey: _settings.PaLM.ApiKey);
|
||||
_tokenStatistics.StartTimer();
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public class KnowledgeService : IKnowledgeService
|
|||
sb.AppendLine("ANSWER: ");
|
||||
prompt = sb.ToString().Trim();
|
||||
|
||||
var completion = await GetTextCompletion().GetCompletion(prompt);
|
||||
var completion = await GetTextCompletion().GetCompletion(prompt, Guid.Empty.ToString(), Guid.Empty.ToString());
|
||||
return JsonSerializer.Deserialize<List<RetrievedResult>>(completion);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,21 @@ public class TextCompletionProvider : ITextCompletion
|
|||
_tokenStatistics = tokenStatistics;
|
||||
}
|
||||
|
||||
public async Task<string> GetCompletion(string text)
|
||||
public async Task<string> GetCompletion(string text, string agentId, string messageId)
|
||||
{
|
||||
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
|
||||
|
||||
// Before chat completion hook
|
||||
var agent = new Agent()
|
||||
{
|
||||
Id = agentId
|
||||
};
|
||||
var userMessage = new RoleDialogModel(AgentRole.User, text)
|
||||
{
|
||||
MessageId = messageId
|
||||
};
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.BeforeGenerating(new Agent(), new List<RoleDialogModel> { new RoleDialogModel(AgentRole.User, text) })).ToArray());
|
||||
hook.BeforeGenerating(agent, new List<RoleDialogModel> { userMessage })).ToArray());
|
||||
|
||||
var llama = _services.GetRequiredService<LlamaAiModel>();
|
||||
llama.LoadModel(_model);
|
||||
|
|
@ -44,8 +52,13 @@ public class TextCompletionProvider : ITextCompletion
|
|||
_tokenStatistics.StopTimer();
|
||||
|
||||
// After chat completion hook
|
||||
var responseMessage = new RoleDialogModel(AgentRole.Assistant, completion)
|
||||
{
|
||||
CurrentAgentId = agentId,
|
||||
MessageId = messageId
|
||||
};
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, completion), new TokenStatsModel
|
||||
hook.AfterGenerated(responseMessage, new TokenStatsModel
|
||||
{
|
||||
Model = _model
|
||||
})).ToArray());
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Role: You're a customer who is going to buy a pizza.
|
|||
* Your phone number is +16308926431
|
||||
|
||||
Requirments:
|
||||
* Greeting to clerk.
|
||||
* You want to know what kind of pizza do they have.
|
||||
* You want to buy three piece of pizza.
|
||||
* Say Bye if the order is placed and payment is completed.
|
||||
21
tests/BotSharp.Plugin.PizzaBot/Hooks/CommonAgentHook.cs
Normal file
21
tests/BotSharp.Plugin.PizzaBot/Hooks/CommonAgentHook.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using BotSharp.Abstraction.Agents;
|
||||
|
||||
namespace BotSharp.Plugin.PizzaBot.Hooks;
|
||||
|
||||
public class CommonAgentHook : AgentHookBase
|
||||
{
|
||||
public override string SelfId => string.Empty;
|
||||
|
||||
public CommonAgentHook(IServiceProvider services, AgentSettings settings)
|
||||
: base(services, settings)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool OnInstructionLoaded(string template, Dictionary<string, object> dict)
|
||||
{
|
||||
dict["current_date"] = DateTime.Now.ToString("MM/dd/yyyy");
|
||||
dict["current_time"] = DateTime.Now.ToString("hh:mm tt");
|
||||
dict["current_weekday"] = DateTime.Now.DayOfWeek;
|
||||
return base.OnInstructionLoaded(template, dict);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue