Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public class RoleDialogModel : ITrackableMessage
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? FunctionName { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? PostbackFunctionName { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? FunctionArgs { get; set; }

Expand Down Expand Up @@ -95,6 +98,7 @@ public static RoleDialogModel From(RoleDialogModel source,
MessageId = source.MessageId,
FunctionArgs = source.FunctionArgs,
FunctionName = source.FunctionName,
PostbackFunctionName = source.PostbackFunctionName,
RichContent = source.RichContent,
StopCompletion = source.StopCompletion,
Instruction = source.Instruction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,13 @@ public async Task<bool> SendMessage(string agentId,
}

// Persist to storage
_storage.Append(_conversationId, message);
if (!message.StopCompletion)
{
_storage.Append(_conversationId, message);

// Add to thread
dialogs.Add(RoleDialogModel.From(message));
// Add to thread
dialogs.Add(RoleDialogModel.From(message));
}

if (!stopCompletion)
{
Expand Down Expand Up @@ -147,6 +150,12 @@ response.RichContent is RichContent<IRichMessage> template &&
Message = new TextMessage(response.Content)
};

// Patch return function name
if (response.PostbackFunctionName != null)
{
response.FunctionName = response.PostbackFunctionName;
}

var hooks = _services.GetServices<IConversationHook>().ToList();

if (response.Instruction != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public void Append(string conversationId, RoleDialogModel dialog)
var dialogElements = new List<DialogElement>();

// Prevent duplicate record to be inserted
var dialogs = db.GetConversationDialogs(conversationId);
/*var dialogs = db.GetConversationDialogs(conversationId);
if (dialogs.Any(x => x.MetaData.MessageId == dialog.MessageId && x.Content == dialog.Content))
{
return;
}
}*/

if (dialog.Role == AgentRole.Function)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace BotSharp.Core.Routing;
public partial class RoutingService
{
private List<FunctionCallingResponse> _functionCallStack = new List<FunctionCallingResponse>();
public List<FunctionCallingResponse> FunctionCallStack => _functionCallStack;

public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
{
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == name);
Expand All @@ -16,10 +18,9 @@ public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
return false;
}

var originalFunctionName = message.FunctionName;
message.FunctionName = name;
message.Role = AgentRole.Function;
message.FunctionArgs = message.FunctionArgs;
// Clone message
var clonedMessage = RoleDialogModel.From(message);
clonedMessage.FunctionName = name;

var hooks = _services.GetServices<IConversationHook>()
.OrderBy(x => x.Priority)
Expand All @@ -28,21 +29,36 @@ public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
// Before executing functions
foreach (var hook in hooks)
{
await hook.OnFunctionExecuting(message);
await hook.OnFunctionExecuting(clonedMessage);
}

bool result = false;

try
{
result = await function.Execute(message);
result = await function.Execute(clonedMessage);

_functionCallStack.Add(new FunctionCallingResponse
{
Role = AgentRole.Function,
FunctionName = message.FunctionName,
Args = JsonDocument.Parse(message.FunctionArgs ?? "{}"),
Content = message.Content
FunctionName = clonedMessage.FunctionName,
Args = JsonDocument.Parse(clonedMessage.FunctionArgs ?? "{}"),
Content = clonedMessage.Content
});

// After functions have been executed
foreach (var hook in hooks)
{
await hook.OnFunctionExecuted(clonedMessage);
}

// Set result to original message
message.PostbackFunctionName = clonedMessage.PostbackFunctionName;
message.CurrentAgentId = clonedMessage.CurrentAgentId;
message.Content = clonedMessage.Content;
message.StopCompletion = clonedMessage.StopCompletion;
message.RichContent = clonedMessage.RichContent;
message.Data = clonedMessage.Data;
}
catch (JsonException ex)
{
Expand All @@ -63,25 +79,12 @@ public async Task<bool> InvokeFunction(string name, RoleDialogModel message)
message.Content = JsonSerializer.Serialize(message.Data);
}

// After functions have been executed
foreach (var hook in hooks)
{
await hook.OnFunctionExecuted(message);
}

// restore original function name
if (!message.StopCompletion &&
message.FunctionName != originalFunctionName)
{
message.FunctionName = originalFunctionName;
}

// Save to Storage as well
if (!message.StopCompletion && message.FunctionName != "route_to_agent")
/*if (!message.StopCompletion && message.FunctionName != "route_to_agent")
{
var storage = _services.GetRequiredService<IConversationStorage>();
storage.Append(Context.ConversationId, message);
}
}*/

return result;
}
Expand Down
27 changes: 25 additions & 2 deletions src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversati
if (!_convSettings.ShowVerboseLog) return;
}

public override async Task OnFunctionExecuted(RoleDialogModel message)
public override async Task OnFunctionExecuting(RoleDialogModel message)
{
if (message.FunctionName == "route_to_agent")
{
Expand All @@ -109,7 +109,30 @@ public override async Task OnFunctionExecuted(RoleDialogModel message)
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
message.FunctionArgs = message.FunctionArgs ?? "{}";
var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions);
var log = $"*{message.FunctionName}*\r\n```json\r\n{args}\r\n```\r\n=> {message.Content?.Trim()}";
var log = $"{message.FunctionName} <u>executing</u>\r\n```json\r\n{args}\r\n```";

var input = new ContentLogInputModel(conversationId, message)
{
Name = agent?.Name,
AgentId = agent?.Id,
Source = ContentLogSource.FunctionCall,
Log = log
};
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
}

public override async Task OnFunctionExecuted(RoleDialogModel message)
{
if (message.FunctionName == "route_to_agent")
{
return;
}

var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(message.CurrentAgentId);
message.FunctionArgs = message.FunctionArgs ?? "{}";
// var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions);
var log = $"{message.FunctionName} =>\r\n*{message.Content?.Trim()}*";

var input = new ContentLogInputModel(conversationId, message)
{
Expand Down