merge with master

This commit is contained in:
Jicheng Lu 2023-09-03 17:51:13 -05:00
commit 0acf60ba08
22 changed files with 306 additions and 61 deletions

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<VersionPrefix>0.10.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
<PackageIcon>Icon.png</PackageIcon>
</PropertyGroup>

View file

@ -1,4 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
namespace BotSharp.Abstraction.Conversations;

View file

@ -0,0 +1,10 @@
namespace BotSharp.Abstraction.Instructs;
public interface IInstructService
{
Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted);
}

View file

@ -0,0 +1,8 @@
namespace BotSharp.Abstraction.Instructs.Models;
public class InstructResult
{
public string Text { get; set; }
public string Function { get; set; }
public object Data { get; set; }
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>10.0</LangVersion>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<VersionPrefix>0.10.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<PropertyGroup Label="Globals">

View file

@ -10,6 +10,8 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Routing.Services;
@ -58,7 +60,6 @@ public static class BotSharpServiceCollectionExtensions
services.AddScoped<Router>();
services.AddScoped<Reasoner>();
services.AddScoped<IAgentRouting, Router>();
services.AddScoped<Reasoner>();
// Register function callback
services.AddScoped<IFunctionCallback, RouteToAgentFn>();
@ -73,6 +74,8 @@ public static class BotSharpServiceCollectionExtensions
services.AddScoped<IBotSharpRepository, FileRepository>();
}
services.AddScoped<IInstructService, InstructService>();
return services;
}

View file

@ -1,4 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
namespace BotSharp.Core.Conversations.Services;

View file

@ -0,0 +1,50 @@
using BotSharp.Abstraction.Functions;
namespace BotSharp.Core.Instructs;
public partial class InstructService
{
private async Task CallFunctions(RoleDialogModel msg)
{
var hooks = _services.GetServices<IConversationHook>()
.OrderBy(x => x.Priority).ToList();
// Invoke functions
var functions = _services.GetServices<IFunctionCallback>()
.Where(x => x.Name == msg.FunctionName)
.ToList();
if (functions.Count == 0)
{
msg.Content = $"Can't find function implementation of {msg.FunctionName}.";
_logger.LogError(msg.Content);
return;
}
foreach (var fn in functions)
{
// Before executing functions
foreach (var hook in hooks)
{
await hook.OnFunctionExecuting(msg);
}
try
{
// Execute function
await fn.Execute(msg);
}
catch (Exception ex)
{
msg.ExecutionResult = ex.Message;
_logger.LogError(msg.ExecutionResult);
}
// After functions have been executed
foreach (var hook in hooks)
{
await hook.OnFunctionExecuted(msg);
}
}
}
}

View file

@ -0,0 +1,84 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Instructs;
public partial class InstructService : IInstructService
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public InstructService(IServiceProvider services, ILogger<InstructService> logger)
{
_services = services;
_logger = logger;
}
public async Task<bool> ExecuteInstructionRecursively(Agent agent,
List<RoleDialogModel> wholeDialogs,
Func<RoleDialogModel, Task> onMessageReceived,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
var chatCompletion = GetChatCompletion();
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
{
await onMessageReceived(msg);
}, async fn =>
{
var preAgentId = agent.Id;
await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted);
// Function executed has exception
if (fn.ExecutionResult == null || fn.StopCompletion)
{
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, fn.Content));
return;
}
fn.Content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.ExecutionResult;
// Find response template
var templateService = _services.GetRequiredService<IResponseTemplateService>();
var response = await templateService.RenderFunctionResponse(agent.Id, fn);
if (!string.IsNullOrEmpty(response))
{
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, response));
return;
}
// After function is executed, pass the result to LLM to get a natural response
wholeDialogs.Add(fn);
await ExecuteInstructionRecursively(agent,
wholeDialogs,
onMessageReceived,
onFunctionExecuting,
onFunctionExecuted);
});
return result;
}
private async Task HandleFunctionMessage(RoleDialogModel msg,
Func<RoleDialogModel, Task> onFunctionExecuting,
Func<RoleDialogModel, Task> onFunctionExecuted)
{
// Call functions
await onFunctionExecuting(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));
}
}

View file

@ -4,7 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<VersionPrefix>0.10.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -0,0 +1,54 @@
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;
namespace BotSharp.OpenAPI.Controllers;
[Authorize]
[ApiController]
public class InstructModeController : ControllerBase, IApiAdapter
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public InstructModeController(IServiceProvider services,
IUserIdentity user)
{
_services = services;
_user = user;
}
[HttpPost("/instruct/{agentId}")]
public async Task<InstructResult> NewConversation([FromRoute] string agentId,
[FromBody] NewMessageModel 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 =>
{
},
async fnExecuted =>
{
response.Function = fnExecuted.FunctionName;
response.Data = fnExecuted.ExecutionData;
});
return response;
}
}

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.10.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.10.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -1,8 +1,9 @@
# Chatbot UI
[Chatbot UI](https://github.com/mckaywrigley/chatbot-ui) is an open source chat UI for AI models.
[Chatbot UI](https://github.com/SciSharp/chatbot-ui) is an open source chat UI for AI models.
```shell
git clone https://github.com/mckaywrigley/chatbot-ui
git clone https://github.com/SciSharp/chatbot-ui
cd chatbot-ui (change dir to chatbot-ui to find the package.json)
npm i
npm run dev
```

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.9.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>10</LangVersion>
<VersionPrefix>0.9.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>

View file

@ -4,10 +4,11 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<VersionPrefix>0.11.0</VersionPrefix>
<VersionPrefix>0.10.1</VersionPrefix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="TensorFlow.Keras" Version="0.11.2" />
</ItemGroup>

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using BotSharp.Plugin.RoutingSpeeder.Providers;
using BotSharp.Plugin.RoutingSpeeder.Providers.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace BotSharp.Plugin.RoutingSpeeder.Controllers;
[AllowAnonymous]
public class RoutingSpeederController : ControllerBase
{
private readonly IServiceProvider _service;
public RoutingSpeederController(IServiceProvider service)
{
_service = service;
}
[HttpPost("/routing-speeder/classifier/train")]
public IActionResult TrainIntentClassifier(TrainingParams trainingParams)
{
var intentClassifier = _service.GetRequiredService<IntentClassifier>();
intentClassifier.InitClassifer(trainingParams.Inference);
intentClassifier.Train(trainingParams);
return Ok(intentClassifier.Labels);
}
}

View file

@ -11,31 +11,46 @@ using Tensorflow.Keras.Callbacks;
using System.Text.RegularExpressions;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Plugin.RoutingSpeeder.Providers.Models;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Tensorflow.Keras;
using BotSharp.Abstraction.Knowledges.Settings;
using System.Numerics;
using Newtonsoft.Json;
using Tensorflow.Keras.Layers;
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Knowledges;
namespace BotSharp.Plugin.RoutingSpeeder.Providers;
public class IntentClassifier
{
private readonly IServiceProvider _services;
private KnowledgeBaseSettings _knowledgeBaseSettings;
Model _model;
public Model model => _model;
private bool _isModelReady;
public bool isModelReady => _isModelReady;
private ClassifierSetting _settings;
public IntentClassifier(IServiceProvider services, ClassifierSetting settings)
private string[] _labels;
public string[] Labels => GetLabels();
private int _numLabels
{
get
{
return Labels.Length;
}
}
public IntentClassifier(IServiceProvider services, ClassifierSetting settings, KnowledgeBaseSettings knowledgeBaseSettings)
{
_services = services;
_settings = settings;
_knowledgeBaseSettings = knowledgeBaseSettings;
}
private void Reset()
@ -50,17 +65,16 @@ public class IntentClassifier
{
return;
}
var vector = _services.GetRequiredService<ITextEmbedding>();
var labels = GetLabels();
var vector = _services.GetServices<ITextEmbedding>()
.FirstOrDefault(x => x.GetType().FullName.EndsWith(_knowledgeBaseSettings.TextEmbedding));
var layers = new List<ILayer>
{
keras.layers.InputLayer((vector.Dimension), name: "Input"),
keras.layers.Dense(256, activation:"relu"),
keras.layers.Dense(256, activation:"relu"),
keras.layers.Dense(labels.Length, activation: keras.activations.Softmax)
keras.layers.Dense(_numLabels, activation: keras.activations.Softmax)
};
_model = keras.Sequential(layers);
@ -90,7 +104,7 @@ public class IntentClassifier
var callbacks = new List<ICallback>() { earlyStop };
var weights = LoadWeights();
var weights = LoadWeights(trainingParams.Inference);
_model.fit(x, y,
batch_size: trainingParams.BatchSize,
@ -104,42 +118,27 @@ public class IntentClassifier
_isModelReady = true;
}
public string LoadWeights()
public string LoadWeights(bool inference = true)
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
var weightsFile = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, $"intent-classifier.h5");
if (File.Exists(weightsFile))
if (File.Exists(weightsFile) && inference)
{
_model.load_weights(weightsFile);
_isModelReady = true;
Console.WriteLine($"Successfully load the weights!");
}
else
{
Console.WriteLine("No available weights.");
var logInfo = inference ? "No available weights." : "Will implement model training process and write trained weights into local";
Console.WriteLine(logInfo);
}
return weightsFile;
}
public (NDArray x, NDArray y) Vectorize(List<DialoguePredictionModel> items)
{
var vector = _services.GetRequiredService<ITextEmbedding>();
var x = np.zeros((items.Count, vector.Dimension), dtype: np.float32);
var y = np.zeros((items.Count, 1), dtype: np.float32);
for (int i = 0; i < items.Count; i++)
{
x[i] = vector.GetVector(TextClean(items[i].text));
if (_settings.LabelMappingDict.ContainsKey(items[i].label))
{
y[i] = _settings.LabelMappingDict[items[i].label];
}
}
return (x, y);
}
public NDArray GetTextEmbedding(string text)
{
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
@ -164,10 +163,10 @@ public class IntentClassifier
var vector = _services.GetRequiredService<ITextEmbedding>();
var vectorList = new List<float[]>();
var labelList = new List<string>();
foreach (var filePath in GetFiles())
{
var texts = File.ReadAllLines(filePath, Encoding.UTF8).Select(x => TextClean(x)).ToList();
@ -192,19 +191,24 @@ public class IntentClassifier
return (x, y);
}
public string[] GetFiles()
public string[] GetFiles(string prefix = "intent")
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
return Directory.GetFiles(rootDirectory).OrderBy(x => x).ToArray();
return Directory.GetFiles(rootDirectory).Where(x => Path.GetFileNameWithoutExtension(x).StartsWith(prefix)).OrderBy(x => x).ToArray();
}
public string[] GetLabels()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, _settings.LABEL_FILE_NAME);
var labelText = File.ReadAllLines(rootDirectory);
return labelText.OrderBy(x => x).ToArray();
if (_labels == null)
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, _settings.LABEL_FILE_NAME);
var labelText = File.ReadAllLines(rootDirectory);
_labels = labelText.OrderBy(x => x).ToArray();
}
return _labels;
}
public string TextClean(string text)
@ -235,24 +239,22 @@ public class IntentClassifier
return string.Empty;
}
var prediction = GetLabels()[probLabel[0]];
var prediction = _labels[probLabel[0]];
return prediction;
}
public void InitClassifer()
public void InitClassifer(bool inference = true)
{
Reset();
Build();
LoadWeights();
LoadWeights(inference);
}
public void Train()
public void Train(TrainingParams trainingParams)
{
var trainingParams = new TrainingParams();
Reset();
(var x, var y) = PrepareLoadData();
Build();
Fit(x, y, trainingParams);
}
}

View file

@ -10,4 +10,5 @@ public class TrainingParams
public int Epochs { get; set; } = 10;
public int BatchSize { get; set; } = 16;
public float LearningRate { get; set; } = 1.0e-4f;
public bool Inference { get; set; } = false;
}

View file

@ -1,8 +1,6 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
@ -10,7 +8,6 @@ using System.Threading.Tasks;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Plugin.RoutingSpeeder.Providers;
using System.Runtime.InteropServices;
using BotSharp.Abstraction.Agents;
using System.IO;
using BotSharp.Abstraction.Routing.Settings;
@ -59,16 +56,20 @@ public class RoutingConversationHook: ConversationHookBase
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
var rootDataPath = agentService.GetDataDir();
string rawDataDir = Path.Combine(rootDataPath, "raw_data", $"{message.CurrentAgentId}.txt");
var lastThreeDialogs = _dialogs.Where(x => x.Role == AgentRole.User).Select(x => x.Content).Reverse().Take(3).ToArray();
string rawDataDir = Path.Combine(rootDataPath, "raw_data", $"agent.{message.CurrentAgentId}.txt");
var lastThreeDialogs = _dialogs.Where(x => x.Role == AgentRole.User || x.Role == AgentRole.Assistant)
.Select(x => x.Content.Replace('\r', ' ').Replace('\n', ' '))
.TakeLast(3)
.ToArray();
var content = string.Join(' ', lastThreeDialogs) + Environment.NewLine;
if (!File.Exists(rawDataDir))
{
await File.WriteAllLinesAsync(rawDataDir, lastThreeDialogs);
await File.WriteAllTextAsync(rawDataDir, content);
}
else
{
await File.AppendAllLinesAsync(rawDataDir, lastThreeDialogs);
await File.AppendAllTextAsync(rawDataDir, content);
}
}
}