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
@@ -1,5 +1,3 @@
using BotSharp.Abstraction.MLTasks;

namespace BotSharp.Abstraction.Conversations;

public interface IConversationService
Expand All @@ -11,8 +9,6 @@ public interface IConversationService
Task<List<Conversation>> GetConversations();
Task DeleteConversation(string id);

IChatCompletion GetChatCompletion();

/// <summary>
/// Send message to LLM
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class RoleDialogModel
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public string Content { get; set; }
public string CurrentAgentId { get; set; }
public string ModelName { get; set; } = "gpt-3.5-turbo";
public float Temperature { get; set; } = 0.5f;

/// <summary>
/// Function name if LLM response function call
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using BotSharp.Abstraction.Conversations.Models;

namespace BotSharp.Abstraction.MLTasks;

public interface IChatCompletion
{
string ModelName { get; }
Task<bool> GetChatCompletionsAsync(Agent agent,
List<RoleDialogModel> conversations,
Func<RoleDialogModel, Task> onMessageReceived,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ namespace BotSharp.Abstraction.Routing.Models;
public class RoutingRecord
{
[JsonPropertyName("agent_id")]
public string AgentId { get; set; }
public string AgentId { get; set; } = string.Empty;

[JsonPropertyName("name")]
public string Name { get; set; }
public string Name { get; set; } = string.Empty;

[JsonPropertyName("description")]
public string Description { get; set; }
public string Description { get; set; } = string.Empty;

[JsonPropertyName("required")]
public List<string> RequiredFields { get; set; } = new List<string>();

[JsonPropertyName("redirect_to")]
public string RedirectTo { get; set; }
public string? RedirectTo { get; set; }

[JsonPropertyName("disabled")]
public bool Disabled { get; set; }
Expand Down
5 changes: 5 additions & 0 deletions src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,9 @@
<ProjectReference Include="..\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Functions\" />
<Folder Include="Hooks\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using BotSharp.Abstraction.Functions;
using BotSharp.Core.Functions;
using BotSharp.Core.Hooks;
using BotSharp.Core.Routing;
using BotSharp.Core.Templating;
using Microsoft.AspNetCore.Builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ public partial class ConversationService
{
int currentRecursiveDepth = 0;

private async Task<bool> GetChatCompletionsAsyncRecursively(IChatCompletion chatCompletion,
Agent agent,
private async Task<bool> GetChatCompletionsAsyncRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
var chatCompletion = CompletionProvider.GetChatCompletion(_services, wholeDialogs.Last().ModelName);

currentRecursiveDepth++;
if (currentRecursiveDepth > _settings.MaxRecursiveDepth)
{
Expand All @@ -28,11 +29,16 @@ private async Task<bool> GetChatCompletionsAsyncRecursively(IChatCompletion chat
text = latestResponse.Content.Split("=>").Last();
}

await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, text)
var msg = new RoleDialogModel(AgentRole.Assistant, text)
{
CurrentAgentId = agent.Id,
Channel = wholeDialogs.Last().Channel
}, onMessageReceived);
};

await HandleAssistantMessage(msg, onMessageReceived);

// Add to dialog history
_storage.Append(_conversationId, agent.Id, msg);

return false;
}
Expand Down Expand Up @@ -85,8 +91,7 @@ await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, fn.Content

wholeDialogs.Add(fn);

await GetChatCompletionsAsyncRecursively(chatCompletion,
agent,
await GetChatCompletionsAsyncRecursively(agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
Expand Down Expand Up @@ -115,8 +120,7 @@ await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, response)
// After function is executed, pass the result to LLM to get a natural response
wholeDialogs.Add(fn);

await GetChatCompletionsAsyncRecursively(chatCompletion,
agent,
await GetChatCompletionsAsyncRecursively(agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing;

Expand Down Expand Up @@ -92,9 +91,7 @@ await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, reasonedCo
});
}

var chatCompletion = GetChatCompletion();
var result = await GetChatCompletionsAsyncRecursively(chatCompletion,
agent,
var result = await GetChatCompletionsAsyncRecursively(agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
Expand Down Expand Up @@ -136,16 +133,4 @@ private void SaveStateByArgs(string args)
}
}
}

public IChatCompletion GetChatCompletion()
{
var completions = _services.GetServices<IChatCompletion>();
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(_settings.ChatCompletion));
}

public IChatCompletion GetGpt4ChatCompletion()
{
var completions = _services.GetServices<IChatCompletion>();
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith("GPT4CompletionProvider"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using BotSharp.Abstraction.MLTasks;

namespace BotSharp.Core.Infrastructures;

public class CompletionProvider
{
public static IChatCompletion GetChatCompletion(IServiceProvider services, string modelName = "gpt-3.5-turbo")
{
var completions = services.GetServices<IChatCompletion>();
var settings = services.GetRequiredService<ConversationSetting>();
// completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(settings.ChatCompletion));
return completions.FirstOrDefault(x => x.ModelName == modelName);
}
}
11 changes: 2 additions & 9 deletions src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async Task<InstructResult> ExecuteInstruction(Agent agent,

var wholeDialogs = new List<RoleDialogModel>
{
new RoleDialogModel("user", message.Content)
message
};

// Trigger before completion hooks
Expand Down Expand Up @@ -71,7 +71,7 @@ private async Task<bool> ExecuteInstructionRecursively(Agent agent,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
var chatCompletion = GetChatCompletion();
var chatCompletion = CompletionProvider.GetChatCompletion(_services, wholeDialogs.Last().ModelName);

var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
{
Expand Down Expand Up @@ -124,11 +124,4 @@ private async Task HandleFunctionMessage(RoleDialogModel msg,
await CallFunctions(msg);
await onFunctionExecuted(msg);
}

public IChatCompletion GetChatCompletion()
{
var completions = _services.GetServices<IChatCompletion>();
var settings = _services.GetRequiredService<ConversationSetting>();
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(settings.ChatCompletion));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BotSharp.Core.Hooks;
namespace BotSharp.Core.Routing;

public class ReasoningHook : AgentHookBase
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing.Models;
using System.IO;

namespace BotSharp.Core.Functions;
namespace BotSharp.Core.Routing;

/// <summary>
/// Router calls this function to set the Active Agent according to the context
Expand Down Expand Up @@ -63,26 +62,36 @@ private bool HasMissingRequiredField(RoleDialogModel message, out string agentId
agentId = routingRule.AgentId;

// Check required fields
var jo = JsonSerializer.Deserialize<object>(message.FunctionArgs);
var root = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
bool hasMissingField = false;
string missingFieldName = "";
foreach (var field in routingRule.RequiredFields)
{
if (jo is JsonElement root)
if (!root.EnumerateObject().Any(x => x.Name == field))
{
if (!root.EnumerateObject().Any(x => x.Name == field))
{
message.ExecutionResult = $"missing {field}.";
hasMissingField = true;
break;
}
else if (root.EnumerateObject().Any(x => x.Name == field) &&
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
{
message.ExecutionResult = $"missing {field}.";
hasMissingField = true;
break;
}
message.ExecutionResult = $"missing {field}.";
hasMissingField = true;
missingFieldName = field;
break;
}
else if (root.EnumerateObject().Any(x => x.Name == field) &&
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
{
message.ExecutionResult = $"missing {field}.";
hasMissingField = true;
missingFieldName = field;
break;
}
}

// Check if states contains the field according conversation context.
var states = _services.GetRequiredService<IConversationStateService>();
if (!string.IsNullOrEmpty(states.GetState(missingFieldName)))
{
var value = states.GetState(missingFieldName);
message.FunctionArgs = message.FunctionArgs.Substring(0, message.FunctionArgs.Length - 1) + $", \"{missingFieldName}\": \"{value}\"" + "}";
hasMissingField = false;
missingFieldName = "";
}

if (hasMissingField && !string.IsNullOrEmpty(routingRule.RedirectTo))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BotSharp.Core.Hooks;
namespace BotSharp.Core.Routing;

public class RoutingHook : AgentHookBase
{
Expand Down
17 changes: 2 additions & 15 deletions src/Infrastructure/BotSharp.Core/Routing/Simulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private async Task<RoleDialogModel> SendMessageToReasoner(Agent reasoner)
new RoleDialogModel(AgentRole.User, @"What's the next step, your response must be in JSON format with ""function"" and ""parameters"". ")
};

var chatCompletion = GetGpt4ChatCompletion();
var chatCompletion = CompletionProvider.GetChatCompletion(_services, "gpt-4");

RoleDialogModel response = null;
await chatCompletion.GetChatCompletionsAsync(reasoner, wholeDialogs, async msg
Expand Down Expand Up @@ -111,7 +111,7 @@ private async Task<RoleDialogModel> SendMessageToAgent(string agentId, List<Role
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.LoadAgent(agentId);

var chatCompletion = GetChatCompletion();
var chatCompletion = CompletionProvider.GetChatCompletion(_services, wholeDialogs.Last().ModelName);

RoleDialogModel response = null;
await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg
Expand All @@ -132,19 +132,6 @@ await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg
return response;
}

public IChatCompletion GetChatCompletion()
{
var completions = _services.GetServices<IChatCompletion>();
var settings = _services.GetRequiredService<ConversationSetting>();
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(settings.ChatCompletion));
}

public IChatCompletion GetGpt4ChatCompletion()
{
var completions = _services.GetServices<IChatCompletion>();
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith("GPT4CompletionProvider"));
}

private void SaveStateByArgs(JsonDocument args)
{
var stateService = _services.GetRequiredService<IConversationStateService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ public async Task DeleteConversation([FromRoute] string agentId, [FromRoute] str
[HttpPost("/conversation/{agentId}/{conversationId}")]
public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,
[FromRoute] string conversationId,
[FromBody] NewMessageModel input,
[FromQuery] string? channel = "openapi")
[FromBody] NewMessageModel input)
{
var conv = _services.GetRequiredService<IConversationService>();
conv.SetConversationId(conversationId, channel);
conv.SetConversationId(conversationId, input.Channel);
input.States.ForEach(x => conv.States.SetState(x.Split('=')[0], x.Split('=')[1]));

var response = new MessageResponseModel();
Expand All @@ -53,7 +52,8 @@ public async Task<MessageResponseModel> SendMessage([FromRoute] string agentId,
await conv.SendMessage(agentId,
new RoleDialogModel("user", input.Text)
{
Channel = channel
Channel = input.Channel,
ModelName = input.ModelName
},
async msg =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ public async Task<InstructResult> NewConversation([FromRoute] string agentId,
}

return await instructor.ExecuteInstruction(agent,
new RoleDialogModel(AgentRole.User, input.Text),
new RoleDialogModel(AgentRole.User, input.Text)
{
ModelName = input.ModelName
},
fn => Task.CompletedTask,
fn => Task.CompletedTask,
fn => Task.CompletedTask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ namespace BotSharp.OpenAPI.ViewModels.Conversations;
public class NewMessageModel
{
public string Text { get; set; }
public string ModelName { get; set; } = "gpt-3.5-turbo";
public string Channel { get; set; } = "openapi";

/// <summary>
/// Conversation states from input
Expand Down
Loading