Skip to content

Redefine IFunctionCallback.Execute. #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 14, 2023
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
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;

namespace BotSharp.Abstraction.Conversations;

Expand All @@ -8,8 +9,39 @@ public interface IConversationService
Task<Conversation> GetConversation(string id);
Task<List<Conversation>> GetConversations();
Task DeleteConversation(string id);
Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting);
Task<bool> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs, Func<RoleDialogModel, Task> onMessageReceived);

IChatCompletion GetChatCompletion();

/// <summary>
/// Send message to LLM
/// </summary>
/// <param name="agentId"></param>
/// <param name="conversationId"></param>
/// <param name="lastDalog"></param>
/// <param name="onMessageReceived"></param>
/// <param name="onFunctionExecuting">This delegate is useful when you want to report progress on UI</param>
/// <returns></returns>
Task<bool> SendMessage(string agentId,
string conversationId,
RoleDialogModel lastDalog,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting);

/// <summary>
/// Send message to LLM if frontend passed over the dialog history
/// </summary>
/// <param name="agentId"></param>
/// <param name="conversationId"></param>
/// <param name="wholeDialogs"></param>
/// <param name="onMessageReceived"></param>
/// <param name="onFunctionExecuting">This delegate is useful when you want to report progress on UI</param>
/// <returns></returns>
Task<bool> SendMessage(string agentId,
string conversationId,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting);

List<RoleDialogModel> GetDialogHistory(string conversationId, int lastCount = 20);
Task CleanHistory(string agentId);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using BotSharp.Abstraction.Conversations.Models;

namespace BotSharp.Abstraction.Functions;

public interface IFunctionCallback
{
string Name { get; }
Task<string> Execute(string args);
Task<bool> Execute(RoleDialogModel message);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,12 @@ public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,

var response = new MessageResponseModel();

await conv.SendMessage(agentId, conversationId, new RoleDialogModel("user", input.Text), async msg =>
{
response.Text += msg.Content;
}, async fn =>
{

});
await conv.SendMessage(agentId, conversationId,
new RoleDialogModel("user", input.Text),
async msg =>
response.Text += msg.Content,
async fn
=> await Task.CompletedTask);

return response;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.MLTasks;
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace BotSharp.Core.Conversations.Services;

Expand Down Expand Up @@ -74,49 +70,26 @@ public async Task<Conversation> NewConversation(Conversation sess)
return record.ToConversation();
}

public async Task<bool> SendMessage(string agentId, string conversationId, RoleDialogModel lastDalog,
public async Task<bool> SendMessage(string agentId, string conversationId,
RoleDialogModel lastDalog,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting)
{
_storage.Append(conversationId, lastDalog);

var wholeDialogs = GetDialogHistory(conversationId);

var response = await SendMessage(agentId, conversationId, wholeDialogs, async msg =>
{
if (msg.Role == "function")
{
// Invoke functions
var functions = _services.GetServices<IFunctionCallback>().Where(x => x.Name == msg.FunctionName);
foreach (var fn in functions)
{
await onFunctionExecuting(msg);

msg.ExecutionResult = await fn.Execute(msg.Content);

var result = msg.ExecutionResult.Replace("\r", " ").Replace("\n", " ");
var content = $"{result}";
// Console.WriteLine($"{msg.Role}: {content}");
_storage.Append(conversationId, new RoleDialogModel(msg.Role, content)
{
FunctionName = msg.FunctionName,
});
}
}
else
{
var content = msg.Content.Replace("\r", " ").Replace("\n", " ");
// Console.WriteLine($"{msg.Role}: {content}");
_storage.Append(conversationId, new RoleDialogModel(msg.Role, content));

await onMessageReceived(msg);
}
});
var response = await SendMessage(agentId, conversationId, wholeDialogs,
onMessageReceived: onMessageReceived,
onFunctionExecuting: onFunctionExecuting);

return response;
}

public async Task<bool> SendMessage(string agentId, string conversationId, List<RoleDialogModel> wholeDialogs, Func<RoleDialogModel, Task> onMessageReceived)
public async Task<bool> SendMessage(string agentId, string conversationId,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting)
{
var converation = await GetConversation(conversationId);

Expand Down Expand Up @@ -152,8 +125,6 @@ public async Task<bool> SendMessage(string agentId, string conversationId, List<
});
}*/

var chatCompletion = GetChatCompletion();

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

// Before chat completion hook
Expand All @@ -166,48 +137,83 @@ public async Task<bool> SendMessage(string agentId, string conversationId, List<
await hook.BeforeCompletion();
}

var chatCompletion = GetChatCompletion();
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
{
if (msg.Role == "function")
{
// Before executing functions
foreach (var hook in hooks)
{
await hook.OnFunctionExecuting(msg);
}
// Save states
var jo = JsonSerializer.Deserialize<object>(msg.Content);
if (jo is JsonElement root)
{
foreach (JsonProperty property in root.EnumerateObject())
{
stateService.SetState(property.Name, property.Value.ToString());
}
}
SaveStateByArgs(msg.Content);

// Call functions
await onFunctionExecuting(msg);
await CallFunctions(conversationId, msg);
}
else
{
// Add to dialog history
_storage.Append(conversationId, new RoleDialogModel(msg.Role, msg.Content));

// After chat completion hook
foreach (var hook in hooks)
{
await hook.AfterCompletion(msg);
}
}
await onMessageReceived(msg);

if (msg.Role == AgentRole.Function)
{
// After functions have been executed
foreach (var hook in hooks)
{
await hook.OnFunctionExecuted(msg);
}
await onMessageReceived(msg);
}
});

return result;
}

private void SaveStateByArgs(string args)
{
var stateService = _services.GetRequiredService<IConversationStateService>();
var jo = JsonSerializer.Deserialize<object>(args);
if (jo is JsonElement root)
{
foreach (JsonProperty property in root.EnumerateObject())
{
stateService.SetState(property.Name, property.Value.ToString());
}
}
}

private async Task CallFunctions(string conversationId, RoleDialogModel msg)
{
var hooks = _services.GetServices<IConversationHook>().ToList();

// Invoke functions
var functions = _services.GetServices<IFunctionCallback>()
.Where(x => x.Name == msg.FunctionName)
.ToList();

foreach (var fn in functions)
{
// Before executing functions
foreach (var hook in hooks)
{
await hook.OnFunctionExecuting(msg);
}

// Execute function
await fn.Execute(msg);

// Add to dialog history
_storage.Append(conversationId, new RoleDialogModel(msg.Role, msg.ExecutionResult)
{
FunctionName = msg.FunctionName,
});

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

public IChatCompletion GetChatCompletion()
{
var completions = _services.GetServices<IChatCompletion>();
Expand All @@ -222,6 +228,8 @@ public Task CleanHistory(string agentId)
public List<RoleDialogModel> GetDialogHistory(string conversationId, int lastCount = 20)
{
var dialogs = _storage.GetDialogs(conversationId);
return dialogs.TakeLast(lastCount).ToList();
return dialogs
.Where(x => x.CreatedAt > DateTime.UtcNow.AddHours(-8))
.TakeLast(lastCount).ToList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void SetState(string name, string value)
{
var currentValue = value;
_state[name] = currentValue;
_logger.LogInformation($"Set state: {name} - {value}");
_logger.LogInformation($"Set state: {name} = {value}");
foreach (var hook in hooks)
{
hook.OnStateChanged(name, preValue, currentValue).Wait();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ namespace BotSharp.Core.Conversations.Services;

public class ConversationStorage : IConversationStorage
{
private readonly IAgentService _agent;
private readonly MyDatabaseSettings _dbSettings;
public ConversationStorage(IAgentService agent, MyDatabaseSettings dbSettings)
public ConversationStorage(MyDatabaseSettings dbSettings)
{
_agent = agent;
_dbSettings = dbSettings;
}

Expand All @@ -18,7 +16,15 @@ public void Append(string conversationId, RoleDialogModel dialog)
var conversationFile = GetStorageFile(conversationId);
var sb = new StringBuilder();
sb.AppendLine($"{dialog.Role}|{dialog.CreatedAt}|{dialog.FunctionName}");
sb.AppendLine($" - {dialog.Content}");

var content = dialog.Content.Trim().Replace("\r", " ").Replace("\n", " ");
if (string.IsNullOrEmpty(content))
{
return;
}

sb.AppendLine($" - {content}");

var conversation = sb.ToString();
File.AppendAllText(conversationFile, conversation);
}
Expand Down
4 changes: 3 additions & 1 deletion src/Infrastructure/BotSharp.Core/Using.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
global using System.Text;
global using System.Threading.Tasks;
global using System.Linq;
global using System.Text.Json;
global using Microsoft.Extensions.DependencyInjection;
global using BotSharp.Abstraction.Plugins;
global using Microsoft.Extensions.Logging;
global using EntityFrameworkCore.BootKit;
global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Knowledges;
Expand Down
4 changes: 3 additions & 1 deletion src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public async Task SendMessage([FromBody] OpenAiMessageInput input)
input.ConversationId,
conversations,
async msg =>
await OnChunkReceived(outputStream, msg));
await OnChunkReceived(outputStream, msg),
async fn
=> await Task.CompletedTask);

await OnEventCompleted(outputStream);
}
Expand Down