Merge pull request #345 from SciSharp/master

merge latest code
This commit is contained in:
geffzhang 2024-03-18 07:57:36 +08:00 committed by GitHub
commit dec1fba4b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 99 additions and 35 deletions

View file

@ -59,6 +59,9 @@ public abstract class ConversationHookBase : IConversationHook
public virtual Task OnMessageReceived(RoleDialogModel message)
=> Task.CompletedTask;
public virtual Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
=> Task.CompletedTask;
public virtual Task OnResponseGenerated(RoleDialogModel message)
=> Task.CompletedTask;

View file

@ -46,6 +46,7 @@ public interface IConversationHook
Task OnStateChanged(string name, string preValue, string currentValue);
Task OnMessageReceived(RoleDialogModel message);
Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg);
/// <summary>
/// Triggered before LLM calls function.

View file

@ -30,6 +30,7 @@ public interface IConversationService
/// <returns></returns>
Task<bool> SendMessage(string agentId,
RoleDialogModel lastDalog,
PostbackMessageModel? replyMessage,
Func<RoleDialogModel, Task> onResponseReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);

View file

@ -4,4 +4,9 @@ public class IncomingMessageModel : MessageConfig
{
public string Text { get; set; } = string.Empty;
public virtual string Channel { get; set; } = string.Empty;
/// <summary>
/// Postback message
/// </summary>
public PostbackMessageModel? Postback { get; set; }
}

View file

@ -0,0 +1,11 @@
namespace BotSharp.Abstraction.Conversations.Models;
public class PostbackMessageModel
{
public string FunctionName { get; set; } = string.Empty;
public string Payload { get; set; } = string.Empty;
/// <summary>
/// Parent message id
/// </summary>
public string ParentId { get; set; } = string.Empty;
}

View file

@ -31,7 +31,7 @@ public interface IRoutingService
List<RoutingHandlerDef> GetHandlers(Agent router);
void ResetRecursiveCounter();
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);
Task<bool> InvokeFunction(string name, RoleDialogModel message, bool restoreOriginalFunctionName = true);
Task<bool> InvokeFunction(string name, RoleDialogModel message);
Task<RoleDialogModel> InstructLoop(RoleDialogModel message);
/// <summary>

View file

@ -10,6 +10,7 @@ public partial class ConversationService
{
public async Task<bool> SendMessage(string agentId,
RoleDialogModel message,
PostbackMessageModel? replyMessage,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
@ -51,7 +52,14 @@ public partial class ConversationService
hook.SetAgent(agent)
.SetConversation(conversation);
await hook.OnMessageReceived(message);
if (replyMessage == null)
{
await hook.OnMessageReceived(message);
}
else
{
await hook.OnPostbackMessageReceived(message, replyMessage);
}
// Interrupted by hook
if (message.StopCompletion)

View file

@ -97,9 +97,10 @@ public class EvaluatingService : IEvaluatingService
await conv.SendMessage(agentId,
new RoleDialogModel(AgentRole.User, text),
replyMessage: null,
async msg => response = msg,
fnExecuting => Task.CompletedTask,
fnExecuted => Task.CompletedTask);
_ => Task.CompletedTask,
_ => Task.CompletedTask);
return response;
}

View file

@ -10,11 +10,11 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
{
new ParameterPropertyDef("next_action_reason", "the reason why route to this agent")
new ParameterPropertyDef("next_action_reason", "the reason why route to this agent, if user is replying last agent's question, you must route to this agent")
{
Required = true
},
new ParameterPropertyDef("next_action_agent", "agent for next action based on user latest response")
new ParameterPropertyDef("next_action_agent", "agent for next action based on user latest response, if user is replying last agent's question, you must route to this agent")
{
Required = true
},

View file

@ -1,3 +1,4 @@
using Amazon.Runtime.Internal.Transform;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories.Filters;
@ -111,9 +112,11 @@ public class NaivePlanner : IPlaner
{
var template = router.Templates.First(x => x.Name == "planner_prompt.naive").Content;
var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ "expected_next_action_agent", states.GetState("expected_next_action_agent")}
});
}

View file

@ -4,7 +4,7 @@ namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public async Task<bool> InvokeFunction(string name, RoleDialogModel message, bool restoreOriginalFunctionName = true)
public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
{
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == name);
if (function == null)
@ -57,8 +57,7 @@ public partial class RoutingService
// restore original function name
if (!message.StopCompletion &&
message.FunctionName != originalFunctionName &&
restoreOriginalFunctionName)
message.FunctionName != originalFunctionName)
{
message.FunctionName = originalFunctionName;
}

View file

@ -1,3 +1,7 @@
What is the next step based on the CONVERSATION?
Route to the Agent that last handled the conversation if necessary.
What is the next step based on the CONVERSATION?
Route to the appropriate agent last handled agent based on the context.
{% if expected_next_action_agent != empty -%}
Expected next action agent is {{ expected_next_action_agent }}.
{%- endif %}
Try to keep the User Goal Agent be consistent as previous goal agent.
If user wants to speak to customer service, use function human_intervention_needed.

View file

@ -171,6 +171,7 @@ public class ConversationController : ControllerBase
var response = new ChatResponseModel();
await conv.SendMessage(agentId, inputMsg,
replyMessage: input.Postback,
async msg =>
{
response.Text = msg.Content;
@ -179,14 +180,8 @@ public class ConversationController : ControllerBase
response.Instruction = msg.Instruction;
response.Data = msg.Data;
},
async fnExecuting =>
{
},
async fnExecuted =>
{
});
_ => Task.CompletedTask,
_ => Task.CompletedTask);
var state = _services.GetRequiredService<IConversationStateService>();
response.States = state.GetStates();

View file

@ -59,6 +59,22 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
}
public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
{
var conversationId = _state.GetConversationId();
var log = $"{message.Content}";
var replyContent = JsonSerializer.Serialize(replyMsg, _serializerOptions);
log += $"\r\n```json\r\n{replyContent}\r\n```";
var input = new ContentLogInputModel(conversationId, message)
{
Name = _user.UserName,
Source = ContentLogSource.UserInput,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
}
public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations)
{
if (!_convSettings.ShowVerboseLog) return;

View file

@ -80,13 +80,12 @@ public class ChatbotUiController : ControllerBase
.SetState("sampling_factor", input.SamplingFactor);
var result = await conv.SendMessage(input.AgentId,
message,
message,
replyMessage: null,
async msg =>
await OnChunkReceived(outputStream, msg),
async fn
=> await Task.CompletedTask,
async fn
=> await Task.CompletedTask);
_ => Task.CompletedTask,
_ => Task.CompletedTask);
await OnEventCompleted(outputStream);
}

View file

@ -60,7 +60,9 @@ public class MessageHandleService
var replies = new List<IRichMessage>();
var result = await conv.SendMessage(agentId,
new RoleDialogModel(AgentRole.User, message), async msg =>
new RoleDialogModel(AgentRole.User, message),
replyMessage: null,
async msg =>
{
if (msg.RichContent != null)
{

View file

@ -59,6 +59,7 @@ public class TwilioVoiceController : TwilioController
var result = await conv.SendMessage(agentId,
new RoleDialogModel(AgentRole.User, input.SpeechResult),
replyMessage: null,
async msg =>
{
response = twilio.ReturnInstructions(msg.Content);

View file

@ -61,16 +61,15 @@ namespace BotSharp.Plugin.WeChat
AgentId = AgentId
}))?.Id;
var result = await conversationService.SendMessage(AgentId, new RoleDialogModel("user", message), async msg =>
{
await ReplyTextMessageAsync(openid, msg.Content);
}, async functionExecuting =>
{
}, async functionExecuted =>
{
});
var result = await conversationService.SendMessage(AgentId,
new RoleDialogModel("user", message),
replyMessage: null,
async msg =>
{
await ReplyTextMessageAsync(openid, msg.Content);
},
_ => Task.CompletedTask,
_ => Task.CompletedTask);
}
private async Task<User> GetWeChatAccountUserAsync(string openId, IServiceProvider service)

View file

@ -0,0 +1,16 @@
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
namespace BotSharp.Plugin.PizzaBot.Hooks;
public class PizzaTypeConversationHook : ConversationHookBase
{
public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
{
if (replyMsg.FunctionName == "get_pizza_types")
{
// message.StopCompletion = true;
}
return;
}
}