merge with master

This commit is contained in:
Jicheng Lu 2023-09-08 12:23:44 -05:00
commit a84f58af78
12 changed files with 153 additions and 62 deletions

View file

@ -0,0 +1,9 @@
using BotSharp.Abstraction.Instructs.Models;
namespace BotSharp.Abstraction.Instructs;
public interface IInstructHook
{
Task BeforeCompletion(RoleDialogModel message);
Task AfterCompletion(InstructResult result);
}

View file

@ -1,9 +1,11 @@
using BotSharp.Abstraction.Instructs.Models;
namespace BotSharp.Abstraction.Instructs;
public interface IInstructService
{
Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Task<InstructResult> ExecuteInstruction(Agent agent,
RoleDialogModel message,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);

View file

@ -0,0 +1,16 @@
using BotSharp.Abstraction.Instructs.Models;
namespace BotSharp.Abstraction.Instructs;
public class InstructHookBase : IInstructHook
{
public virtual async Task AfterCompletion(InstructResult result)
{
return;
}
public virtual async Task BeforeCompletion(RoleDialogModel message)
{
return;
}
}

View file

@ -5,6 +5,9 @@ namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
#if !DEBUG
[MemoryCache(10 * 60)]
#endif
public async Task<Agent> LoadAgent(string id)
{
var hooks = _services.GetServices<IAgentHook>();

View file

@ -76,6 +76,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aspects.Cache" Version="2.0.3" />
<PackageReference Include="EntityFrameworkCore.BootKit" Version="6.2.1" />
<PackageReference Include="Fluid.Core" Version="2.4.0" />
<PackageReference Include="TensorFlow.Keras" Version="0.11.2" />

View file

@ -59,6 +59,21 @@ public partial class ConversationService
}, onMessageReceived);
return;
}
else if (fn.StopCompletion)
{
var message = new RoleDialogModel(AgentRole.Assistant, fn.Content)
{
CurrentAgentId = fn.CurrentAgentId,
Channel = fn.Channel,
ExecutionData = fn.ExecutionData,
ExecutionResult = fn.ExecutionResult
};
await HandleAssistantMessage(message, onMessageReceived);
_storage.Append(_conversationId, agent.Id, message);
return;
}
fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.ExecutionResult;

View file

@ -1,8 +1,10 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Templating;
using System.IO;
namespace BotSharp.Core.Instructs;
@ -17,7 +19,53 @@ public partial class InstructService : IInstructService
_logger = logger;
}
public async Task<bool> ExecuteInstructionRecursively(Agent agent,
public async Task<InstructResult> ExecuteInstruction(Agent agent,
RoleDialogModel message,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
var response = new InstructResult();
var wholeDialogs = new List<RoleDialogModel>
{
new RoleDialogModel("user", message.Content)
};
// Trigger before completion hooks
var hooks = _services.GetServices<IInstructHook>();
foreach (var hook in hooks)
{
await hook.BeforeCompletion(message);
}
await ExecuteInstructionRecursively(agent,
wholeDialogs,
async msg =>
{
response.Text = msg.Content;
await onMessageReceived(msg);
},
async fn =>
{
response.Function = fn.FunctionName;
await onFunctionExecuting(fn);
},
async fn =>
{
response.Data = fn.ExecutionData;
await onFunctionExecuted(fn);
});
foreach (var hook in hooks)
{
await hook.AfterCompletion(response);
}
return response;
}
private async Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
@ -28,6 +76,8 @@ public partial class InstructService : IInstructService
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
{
await onMessageReceived(msg);
wholeDialogs.Add(msg);
}, async fn =>
{
var preAgentId = agent.Id;

View file

@ -1,3 +1,4 @@
using Aspects.Cache;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
@ -29,7 +30,8 @@ public class Router : IAgentRouting
return await agentService.LoadAgent(AgentId);
}
public RoutingItem[] GetRoutingRecords()
[MemoryCache(10 * 60)]
public RoutingRecord[] GetRoutingRecords()
{
var db = _services.GetRequiredService<IBotSharpRepository>();

View file

@ -20,4 +20,5 @@ global using BotSharp.Core.Repository;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Core.Users.Services;
global using BotSharp.Core.Users.Services;
global using Aspects.Cache;

View file

@ -1,9 +1,10 @@
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.OpenAPI.ViewModels.Conversations;
using BotSharp.OpenAPI.ViewModels.Instructs;
namespace BotSharp.OpenAPI.Controllers;
@ -12,43 +13,32 @@ namespace BotSharp.OpenAPI.Controllers;
public class InstructModeController : ControllerBase, IApiAdapter
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public InstructModeController(IServiceProvider services,
IUserIdentity user)
public InstructModeController(IServiceProvider services)
{
_services = services;
_user = user;
}
[HttpPost("/instruct/{agentId}")]
public async Task<InstructResult> NewConversation([FromRoute] string agentId,
[FromBody] NewMessageModel input)
[FromBody] InstructMessageModel input)
{
var response = new InstructResult();
var instructor = _services.GetRequiredService<IInstructService>();
var agentService = _services.GetRequiredService<IAgentService>();
Agent agent = await agentService.LoadAgent(agentId);
await instructor.ExecuteInstructionRecursively(agent,
new List<RoleDialogModel>
{
new RoleDialogModel("user", input.Text)
},
async msg =>
{
response.Text = msg.Content;
},
async fnExecuting =>
{
// switch to different instruction template
if (!string.IsNullOrEmpty(input.TemplateName))
{
var agentSettings = _services.GetRequiredService<AgentSettings>();
var filePath = Path.Combine(agentService.GetAgentDataDir(agentId), $"{input.TemplateName}.{agentSettings.TemplateFormat}");
agent.Instruction = System.IO.File.ReadAllText(filePath);
}
},
async fnExecuted =>
{
response.Function = fnExecuted.FunctionName;
response.Data = fnExecuted.ExecutionData;
});
return response;
return await instructor.ExecuteInstruction(agent,
new RoleDialogModel(AgentRole.User, input.Text),
fn => Task.CompletedTask,
fn => Task.CompletedTask,
fn => Task.CompletedTask);
}
}

View file

@ -0,0 +1,8 @@
using BotSharp.OpenAPI.ViewModels.Conversations;
namespace BotSharp.OpenAPI.ViewModels.Instructs;
public class InstructMessageModel : NewMessageModel
{
public string? TemplateName { get; set; }
}

View file

@ -7,7 +7,6 @@ using Tensorflow.Keras.Engine;
using Tensorflow.NumPy;
using static Tensorflow.Binding;
using Tensorflow.Keras.Callbacks;
using System.Text.RegularExpressions;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Knowledges.Settings;
@ -151,11 +150,11 @@ public class IntentClassifier
.ServiceProvider
.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(
agentService.GetDataDir(),
agentService.GetDataDir(),
_settings.RAW_DATA_DIR);
string saveLabelDirectory = Path.Combine(
agentService.GetDataDir(),
_settings.MODEL_DIR,
agentService.GetDataDir(),
_settings.MODEL_DIR,
_settings.LABEL_FILE_NAME);
if (!Directory.Exists(rootDirectory))
@ -170,18 +169,15 @@ public class IntentClassifier
foreach (var filePath in GetFiles())
{
var texts = File.ReadAllLines(filePath, Encoding.UTF8)
.Select(x => TextClean(x))
.ToList();
var texts = File.ReadAllLines(filePath, Encoding.UTF8).ToList();
vectorList.AddRange(vector.GetVectors(texts));
string fileName = Path.GetFileNameWithoutExtension(filePath);
labelList.AddRange(Enumerable.Repeat(fileName, texts.Count).ToList());
}
// Write label into local file
// Sort label to keep the same order
var uniqueLabelList = labelList.Distinct().OrderBy(x => x).ToArray();
File.WriteAllLines(saveLabelDirectory, uniqueLabelList);
var x = np.zeros((vectorList.Count, vector.Dimension), dtype: np.float32);
var y = np.zeros((vectorList.Count, 1), dtype: np.float32);
@ -195,13 +191,20 @@ public class IntentClassifier
return (x, y);
}
public string[] GetFiles(string prefix = "intent")
public string[] GetFiles(string prefix = "")
{
var agentService = _services.CreateScope()
.ServiceProvider
.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
if (string.IsNullOrEmpty(prefix))
{
return Directory.GetFiles(rootDirectory)
.OrderBy(x => Path.GetFileName(x).Split(".")[^2])
.ToArray();
}
return Directory.GetFiles(rootDirectory)
.Where(x => Path.GetFileNameWithoutExtension(x)
.StartsWith(prefix))
@ -216,32 +219,24 @@ public class IntentClassifier
var agentService = _services.CreateScope()
.ServiceProvider
.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(
agentService.GetDataDir(),
string[] labels = GetFiles()
.Select(x => Path.GetFileName(x).Split(".")[^2])
.ToArray();
string writePath = Path.Combine(
agentService.GetDataDir(),
_settings.MODEL_DIR,
_settings.LABEL_FILE_NAME
);
_settings.LABEL_FILE_NAME);
var labelText = File.ReadAllLines(rootDirectory);
_labels = labelText.OrderBy(x => x).ToArray();
_labels = labels.OrderBy(x => x).ToArray();
// Write labels into the local txt file
File.WriteAllLines(writePath, _labels);
}
return _labels;
}
public string TextClean(string text)
{
// Remove punctuation
// Remove digits
// To lowercase
var processedText = Regex.Replace(text, "[AB0-9]", " ");
var replacedTextList = processedText.Select(c => char.IsPunctuation(c) ? ' ' : c).ToList();
return string.Join("", replacedTextList)
.Replace(" ", " ")
.ToLower();
}
public string Predict(NDArray vector, float confidenceScore = 0.9f)
{
if (!_isModelReady)
@ -260,7 +255,6 @@ public class IntentClassifier
}
var labelIndex = probLabel[0];
return _labels[labelIndex];
}
public void InitClassifer(bool inference = true)