Merge branch 'master' into lida_dev
This commit is contained in:
commit
0c7a59b8b2
|
|
@ -43,4 +43,6 @@ public class PageActionArgs
|
|||
/// Wait time in seconds after page is opened
|
||||
/// </summary>
|
||||
public int WaitTime { get; set; }
|
||||
|
||||
public bool ReadInnerHTMLAsBody { get; set; } = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,4 +79,7 @@ public abstract class ConversationHookBase : IConversationHook
|
|||
|
||||
public virtual Task OnBreakpointUpdated(string conversationId, bool resetStates)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public virtual Task OnNotificationGenerated(RoleDialogModel message)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Abstraction.Conversations.Enums;
|
||||
|
||||
public static class MessageTypeName
|
||||
{
|
||||
public const string Plain = "plain";
|
||||
public const string Notification = "notification";
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations;
|
||||
|
||||
public interface IConversationHook
|
||||
|
|
@ -107,4 +105,11 @@ public interface IConversationHook
|
|||
/// <param name="conversationId"></param>
|
||||
/// <returns></returns>
|
||||
Task OnBreakpointUpdated(string conversationId, bool resetStates);
|
||||
|
||||
/// <summary>
|
||||
/// Generate a notification
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
Task OnNotificationGenerated(RoleDialogModel message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public interface IConversationService
|
|||
IConversationStateService States { get; }
|
||||
string ConversationId { get; }
|
||||
Task<Conversation> NewConversation(Conversation conversation);
|
||||
void SetConversationId(string conversationId, List<MessageState> states);
|
||||
void SetConversationId(string conversationId, List<MessageState> states, bool isReadOnly = false);
|
||||
Task<Conversation> GetConversation(string id);
|
||||
Task<PagedItems<Conversation>> GetConversations(ConversationFilter filter);
|
||||
Task<Conversation> UpdateConversationTitle(string id, string title);
|
||||
|
|
@ -41,7 +41,7 @@ public interface IConversationService
|
|||
PostbackMessageModel? replyMessage,
|
||||
Func<RoleDialogModel, Task> onResponseReceived);
|
||||
|
||||
List<RoleDialogModel> GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true);
|
||||
List<RoleDialogModel> GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true, IEnumerable<string>? includeMessageTypes = null);
|
||||
Task CleanHistory(string agentId);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ namespace BotSharp.Abstraction.Conversations;
|
|||
|
||||
public interface IConversationStorage
|
||||
{
|
||||
void InitStorage(string conversationId);
|
||||
void Append(string conversationId, RoleDialogModel dialog);
|
||||
List<RoleDialogModel> GetDialogs(string conversationId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ public class DialogMetaData
|
|||
[JsonPropertyName("message_id")]
|
||||
public string MessageId { get; set; }
|
||||
|
||||
[JsonPropertyName("message_type")]
|
||||
public string MessageType { get; set; }
|
||||
|
||||
[JsonPropertyName("function_name")]
|
||||
public string? FunctionName { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Conversations.Enums;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Messaging;
|
||||
using BotSharp.Abstraction.Messaging.Models.RichContent;
|
||||
|
|
@ -11,6 +12,11 @@ public class RoleDialogModel : ITrackableMessage
|
|||
/// </summary>
|
||||
public string MessageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The message type
|
||||
/// </summary>
|
||||
public string MessageType { get; set; } = MessageTypeName.Plain;
|
||||
|
||||
/// <summary>
|
||||
/// user, system, assistant, function
|
||||
/// </summary>
|
||||
|
|
@ -101,6 +107,7 @@ public class RoleDialogModel : ITrackableMessage
|
|||
public List<ImageGeneration> GeneratedImages { get; set; } = new List<ImageGeneration>();
|
||||
|
||||
|
||||
|
||||
private RoleDialogModel()
|
||||
{
|
||||
}
|
||||
|
|
@ -110,6 +117,7 @@ public class RoleDialogModel : ITrackableMessage
|
|||
Role = role;
|
||||
Content = text;
|
||||
MessageId = Guid.NewGuid().ToString();
|
||||
MessageType = MessageTypeName.Plain;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
@ -132,6 +140,7 @@ public class RoleDialogModel : ITrackableMessage
|
|||
{
|
||||
CurrentAgentId = source.CurrentAgentId,
|
||||
MessageId = source.MessageId,
|
||||
MessageType = source.MessageType,
|
||||
FunctionArgs = source.FunctionArgs,
|
||||
FunctionName = source.FunctionName,
|
||||
ToolCallId = source.ToolCallId,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ public interface IKnowledgeService
|
|||
/// <param name="contents"></param>
|
||||
/// <param name="refData"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> ImportDocumentContentToKnowledge(string collectionName, string fileName, string fileSource, IEnumerable<string> contents, DocMetaRefData? refData = null);
|
||||
Task<bool> ImportDocumentContentToKnowledge(string collectionName, string fileName, string fileSource, IEnumerable<string> contents,
|
||||
DocMetaRefData? refData = null, Dictionary<string, object>? payload = null);
|
||||
/// <summary>
|
||||
/// Delete one document and its related knowledge in the collection
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ public static class ContentLogSource
|
|||
public const string FunctionCall = "function call";
|
||||
public const string AgentResponse = "agent response";
|
||||
public const string HardRule = "hard rule";
|
||||
public const string Notification = "notification";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
using BotSharp.Abstraction.Processors.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Processors;
|
||||
|
||||
public interface IBaseProcessor<TInput, TOutput> where TInput : LlmBaseRequest where TOutput : class
|
||||
{
|
||||
string Provider { get; }
|
||||
string Name => string.Empty;
|
||||
int Order { get; }
|
||||
|
||||
Task<TOutput> Execute(TInput input);
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace BotSharp.Abstraction.Processors.Models;
|
||||
|
||||
public class LlmBaseRequest
|
||||
{
|
||||
public string Provider { get; set; }
|
||||
public string Model { get; set; }
|
||||
public string? AgentId { get; set; }
|
||||
public string? TemplateName { get; set; }
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using BotSharp.Abstraction.Infrastructures.Enums;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Translations;
|
||||
|
||||
public class TranslationRequestModel
|
||||
|
|
@ -7,3 +5,15 @@ public class TranslationRequestModel
|
|||
public string Text { get; set; } = null!;
|
||||
public string ToLang { get; set; } = LanguageType.CHINESE;
|
||||
}
|
||||
|
||||
public class TranslationScriptTimestamp
|
||||
{
|
||||
public string Text { set; get; } = null!;
|
||||
public string Timestamp { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class TranslationLongTextRequestModel
|
||||
{
|
||||
public TranslationScriptTimestamp[] Texts { get; set; } = null!;
|
||||
public string ToLang { get; set; } = LanguageType.CHINESE;
|
||||
}
|
||||
|
|
@ -33,4 +33,6 @@ public class UserRole
|
|||
/// AI Assistant
|
||||
/// </summary>
|
||||
public const string Assistant = "assistant";
|
||||
|
||||
public const string Root = "root";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ public interface IUserService
|
|||
Task<User> GetMyProfile();
|
||||
Task<bool> VerifyUserNameExisting(string userName);
|
||||
Task<bool> VerifyEmailExisting(string email);
|
||||
Task<bool> SendVerificationCodeResetPassword(User user);
|
||||
Task<bool> SendVerificationCodeResetPasswordNoLogin(User user);
|
||||
Task<bool> SendVerificationCodeResetPasswordLogin();
|
||||
Task<bool> ResetUserPassword(User user);
|
||||
Task<bool> ModifyUserEmail(string email);
|
||||
Task<bool> ModifyUserPhone(string phone);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using BotSharp.Abstraction.Messaging.JsonConverters;
|
|||
using BotSharp.Abstraction.Users.Settings;
|
||||
using BotSharp.Abstraction.Interpreters.Settings;
|
||||
using BotSharp.Abstraction.Infrastructures;
|
||||
using BotSharp.Core.Processors;
|
||||
|
||||
namespace BotSharp.Core;
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ public static class BotSharpCoreExtensions
|
|||
|
||||
services.AddScoped<ISettingService, SettingService>();
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
services.AddScoped<ProcessorFactory>();
|
||||
|
||||
services.AddSingleton<DistributedLocker>();
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public partial class ConversationService
|
|||
}
|
||||
|
||||
// Before chat completion hook
|
||||
hooks = ReOrderConversationHooks(hooks);
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
hook.SetAgent(agent)
|
||||
|
|
@ -173,4 +174,18 @@ public partial class ConversationService
|
|||
// Add to dialog history
|
||||
_storage.Append(_conversationId, response);
|
||||
}
|
||||
|
||||
private List<IConversationHook> ReOrderConversationHooks(List<IConversationHook> hooks)
|
||||
{
|
||||
var target = "ChatHubConversationHook";
|
||||
var chathub = hooks.FirstOrDefault(x => x.GetType().Name == target);
|
||||
var otherHooks = hooks.Where(x => x.GetType().Name != target).ToList();
|
||||
|
||||
if (chathub != null)
|
||||
{
|
||||
var newHooks = new List<IConversationHook> { chathub }.Concat(otherHooks);
|
||||
return newHooks.ToList();
|
||||
}
|
||||
return hooks;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Conversations.Enums;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ public partial class ConversationService
|
|||
|
||||
if (dialogs.IsNullOrEmpty()) continue;
|
||||
|
||||
dialogs = dialogs.Where(x => x.MessageType != MessageTypeName.Notification).ToList();
|
||||
var content = GetConversationContent(dialogs);
|
||||
if (string.IsNullOrWhiteSpace(content)) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ public partial class ConversationService : IConversationService
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<RoleDialogModel> GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true)
|
||||
public List<RoleDialogModel> GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true, IEnumerable<string>? includeMessageTypes = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_conversationId))
|
||||
{
|
||||
|
|
@ -115,6 +115,15 @@ public partial class ConversationService : IConversationService
|
|||
|
||||
var dialogs = _storage.GetDialogs(_conversationId);
|
||||
|
||||
if (!includeMessageTypes.IsNullOrEmpty())
|
||||
{
|
||||
dialogs = dialogs.Where(x => string.IsNullOrEmpty(x.MessageType) || includeMessageTypes.Contains(x.MessageType)).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
dialogs = dialogs.Where(x => string.IsNullOrEmpty(x.MessageType) || x.MessageType.IsEqualTo(MessageTypeName.Plain)).ToList();
|
||||
}
|
||||
|
||||
if (fromBreakpoint)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
|
|
@ -134,7 +143,7 @@ public partial class ConversationService : IConversationService
|
|||
.ToList();
|
||||
}
|
||||
|
||||
public void SetConversationId(string conversationId, List<MessageState> states)
|
||||
public void SetConversationId(string conversationId, List<MessageState> states, bool isReadOnly = false)
|
||||
{
|
||||
_conversationId = conversationId;
|
||||
_state.Load(_conversationId);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ public class ConversationStorage : IConversationStorage
|
|||
Role = dialog.Role,
|
||||
AgentId = agentId,
|
||||
MessageId = dialog.MessageId,
|
||||
MessageType = dialog.MessageType,
|
||||
FunctionName = dialog.FunctionName,
|
||||
CreateTime = dialog.CreatedAt
|
||||
};
|
||||
|
|
@ -65,6 +66,7 @@ public class ConversationStorage : IConversationStorage
|
|||
Role = dialog.Role,
|
||||
AgentId = agentId,
|
||||
MessageId = dialog.MessageId,
|
||||
MessageType = dialog.MessageType,
|
||||
SenderId = dialog.SenderId,
|
||||
FunctionName = dialog.FunctionName,
|
||||
CreateTime = dialog.CreatedAt
|
||||
|
|
@ -108,6 +110,7 @@ public class ConversationStorage : IConversationStorage
|
|||
var role = meta.Role;
|
||||
var currentAgentId = meta.AgentId;
|
||||
var messageId = meta.MessageId;
|
||||
var messageType = meta.MessageType;
|
||||
var function = meta.FunctionName;
|
||||
var senderId = role == AgentRole.Function ? currentAgentId : meta.SenderId;
|
||||
var createdAt = meta.CreateTime;
|
||||
|
|
@ -120,6 +123,7 @@ public class ConversationStorage : IConversationStorage
|
|||
{
|
||||
CurrentAgentId = currentAgentId,
|
||||
MessageId = messageId,
|
||||
MessageType = messageType,
|
||||
CreatedAt = createdAt,
|
||||
SenderId = senderId,
|
||||
FunctionName = function,
|
||||
|
|
@ -143,23 +147,4 @@ public class ConversationStorage : IConversationStorage
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void InitStorage(string conversationId)
|
||||
{
|
||||
var file = GetStorageFile(conversationId);
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
File.WriteAllLines(file, new string[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetStorageFile(string conversationId)
|
||||
{
|
||||
var dir = Path.Combine(_dbSettings.FileRepository, "conversations", conversationId);
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
return Path.Combine(dir, "dialogs.txt");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
using BotSharp.Abstraction.Processors;
|
||||
using BotSharp.Abstraction.Processors.Models;
|
||||
|
||||
namespace BotSharp.Core.Processors;
|
||||
|
||||
public class ProcessorFactory
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public ProcessorFactory(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public IEnumerable<IBaseProcessor<TInput, TOutput>> Create<TInput, TOutput>(string provider)
|
||||
where TInput : LlmBaseRequest where TOutput : class
|
||||
{
|
||||
var processors = _services.GetServices<IBaseProcessor<TInput, TOutput>>();
|
||||
processors = processors.Where(x => x.Provider == provider);
|
||||
return processors.OrderBy(x => x.Order);
|
||||
}
|
||||
|
||||
public IBaseProcessor<TInput, TOutput>? Create<TInput, TOutput>(string provider, string name)
|
||||
where TInput : LlmBaseRequest where TOutput : class
|
||||
{
|
||||
var processors = _services.GetServices<IBaseProcessor<TInput, TOutput>>();
|
||||
return processors.FirstOrDefault(x => x.Provider == provider && x.Name == name);
|
||||
}
|
||||
}
|
||||
|
|
@ -101,12 +101,12 @@ public class TranslationService : ITranslationService
|
|||
{
|
||||
var translatedStringList = await InnerTranslate(texts, language, template);
|
||||
|
||||
int retry = 0;
|
||||
/*int retry = 0;
|
||||
while (translatedStringList.Texts.Length != texts.Count && retry < 3)
|
||||
{
|
||||
translatedStringList = await InnerTranslate(texts, language, template);
|
||||
retry++;
|
||||
}
|
||||
}*/
|
||||
|
||||
// Override language if it's Unknown, it's used to output the corresponding language.
|
||||
var states = _services.GetRequiredService<IConversationStateService>();
|
||||
|
|
@ -119,7 +119,7 @@ public class TranslationService : ITranslationService
|
|||
var translatedTexts = translatedStringList.Texts;
|
||||
var memoryInputs = new List<TranslationMemoryInput>();
|
||||
|
||||
for (var i = 0; i < texts.Count; i++)
|
||||
for (var i = 0; i < Math.Min(texts.Count, translatedTexts.Length); i++)
|
||||
{
|
||||
map[outOfMemoryList[i].OriginalText] = translatedTexts[i].Text;
|
||||
memoryInputs.Add(new TranslationMemoryInput
|
||||
|
|
@ -375,6 +375,8 @@ public class TranslationService : ITranslationService
|
|||
var render = _services.GetRequiredService<ITemplateRender>();
|
||||
var prompt = render.Render(template, translator.TemplateDict);
|
||||
|
||||
_logger.LogInformation($"Translation prompt: {prompt}");
|
||||
|
||||
var translationDialogs = new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, prompt)
|
||||
|
|
@ -384,6 +386,8 @@ public class TranslationService : ITranslationService
|
|||
}
|
||||
};
|
||||
var response = await _completion.GetChatCompletions(translator, translationDialogs);
|
||||
|
||||
_logger.LogInformation(response.Content);
|
||||
return response.Content.JsonContent<TranslationOutput>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -412,7 +412,48 @@ public class UserService : IUserService
|
|||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> SendVerificationCodeResetPassword(User user)
|
||||
public async Task<bool> SendVerificationCodeResetPasswordNoLogin(User user)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
|
||||
User? record = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(user.Email) && !string.IsNullOrEmpty(user.Phone))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(user.Phone))
|
||||
{
|
||||
record = db.GetUserByPhone(user.Phone);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(user.Email))
|
||||
{
|
||||
record = db.GetUserByEmail(user.Email);
|
||||
}
|
||||
|
||||
if (record == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
record.VerificationCode = Nanoid.Generate(alphabet: "0123456789", size: 6);
|
||||
|
||||
//update current verification code.
|
||||
db.UpdateUserVerificationCode(record.Id, record.VerificationCode);
|
||||
|
||||
//send code to user Email.
|
||||
var hooks = _services.GetServices<IAuthenticationHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
hook.VerificationCodeResetPassword(record);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> SendVerificationCodeResetPasswordLogin()
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
|
||||
|
|
@ -422,23 +463,6 @@ public class UserService : IUserService
|
|||
{
|
||||
record = db.GetUserById(_user.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(user.Email) && !string.IsNullOrEmpty(user.Phone))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(user.Email))
|
||||
{
|
||||
record = db.GetUserByEmail(user.Email);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(user.Phone))
|
||||
{
|
||||
record = db.GetUserByPhone(user.Phone);
|
||||
}
|
||||
}
|
||||
|
||||
if (record == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
{{ text_list }}
|
||||
|
||||
=====
|
||||
{% if language == "Chinese" %}
|
||||
将以上所有句子翻译成中文。
|
||||
|
||||
要求:
|
||||
* 以 JSON 格式输出翻译后的文本 {"input_lang":"原始文本语言", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}。
|
||||
* output_count 必须等于输出中texts数组的长度。
|
||||
{% else %}
|
||||
Translate all the above sentences into {{ language }}.
|
||||
Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}.
|
||||
The "output_count" must equal the length of the "texts" array in the output.
|
||||
|
||||
Requirements:
|
||||
* Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}.
|
||||
* The "output_count" must equal the length of the "texts" array in the output.
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using Azure;
|
||||
using BotSharp.Abstraction.Files.Constants;
|
||||
using BotSharp.Abstraction.Files.Enums;
|
||||
using BotSharp.Abstraction.Options;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
using BotSharp.Core.Infrastructures;
|
||||
|
||||
namespace BotSharp.OpenAPI.Controllers;
|
||||
|
||||
|
|
@ -251,6 +253,44 @@ public class ConversationController : ControllerBase
|
|||
return isSuccess ? newMessageId : string.Empty;
|
||||
}
|
||||
|
||||
#region Send notification
|
||||
[HttpPost("/conversation/{conversationId}/notification")]
|
||||
public async Task<ChatResponseModel> SendNotification([FromRoute] string conversationId, [FromBody] NewMessageModel input)
|
||||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
|
||||
conv.SetConversationId(conversationId, new List<MessageState>(), isReadOnly: true);
|
||||
|
||||
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
|
||||
{
|
||||
MessageId = Guid.NewGuid().ToString(),
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
var response = new ChatResponseModel()
|
||||
{
|
||||
ConversationId = conversationId,
|
||||
MessageId = inputMsg.MessageId,
|
||||
Sender = new UserViewModel
|
||||
{
|
||||
Id = user?.Id ?? string.Empty,
|
||||
FirstName = user?.FirstName ?? string.Empty,
|
||||
LastName = user?.LastName ?? string.Empty
|
||||
},
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await HookEmitter.Emit<IConversationHook>(_services, async hook =>
|
||||
await hook.OnNotificationGenerated(inputMsg)
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Send message
|
||||
[HttpPost("/conversation/{agentId}/{conversationId}")]
|
||||
public async Task<ChatResponseModel> SendMessage([FromRoute] string agentId,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ public class InstructModeController : ControllerBase
|
|||
.SetState("model", input.Model, source: StateSource.External)
|
||||
.SetState("model_id", input.ModelId, source: StateSource.External)
|
||||
.SetState("instruction", input.Instruction, source: StateSource.External)
|
||||
.SetState("input_text", input.Text,source: StateSource.External);
|
||||
.SetState("input_text", input.Text, source: StateSource.External)
|
||||
.SetState("template_name", input.Template, source: StateSource.External);
|
||||
|
||||
var instructor = _services.GetRequiredService<IInstructService>();
|
||||
var result = await instructor.Execute(agentId,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Options;
|
||||
using BotSharp.Abstraction.Translation;
|
||||
using BotSharp.OpenAPI.ViewModels.Translations;
|
||||
|
||||
|
|
@ -9,10 +9,13 @@ namespace BotSharp.OpenAPI.Controllers;
|
|||
public class TranslationController : ControllerBase
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
|
||||
public TranslationController(IServiceProvider services)
|
||||
public TranslationController(IServiceProvider services,
|
||||
BotSharpOptions options)
|
||||
{
|
||||
_services = services;
|
||||
_jsonOptions = InitJsonOptions(options);
|
||||
}
|
||||
|
||||
[HttpPost("/translate")]
|
||||
|
|
@ -21,10 +24,79 @@ public class TranslationController : ControllerBase
|
|||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(BuiltInAgentId.AIAssistant);
|
||||
var translator = _services.GetRequiredService<ITranslationService>();
|
||||
var text = await translator.Translate(agent, Guid.NewGuid().ToString(), model.Text, language: model.ToLang);
|
||||
var states = _services.GetRequiredService<IConversationStateService>();
|
||||
states.SetState("max_tokens", "8192");
|
||||
var text = await translator.Translate(agent, Guid.NewGuid().ToString(), model.Text.Split("\r\n"), language: model.ToLang);
|
||||
return new TranslationResponseModel
|
||||
{
|
||||
Text = text
|
||||
Text = string.Join("\r\n", text)
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("/translate/long-text")]
|
||||
public async Task SendMessageSse([FromBody] TranslationLongTextRequestModel model)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(BuiltInAgentId.AIAssistant);
|
||||
var translator = _services.GetRequiredService<ITranslationService>();
|
||||
|
||||
Response.StatusCode = 200;
|
||||
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream");
|
||||
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, "no-cache");
|
||||
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.Connection, "keep-alive");
|
||||
|
||||
foreach (var script in model.Texts)
|
||||
{
|
||||
var translatedText = await translator.Translate(agent, Guid.NewGuid().ToString(), script.Text, language: model.ToLang);
|
||||
|
||||
var json = JsonSerializer.Serialize(new TranslationScriptTimestamp
|
||||
{
|
||||
Text = translatedText,
|
||||
Timestamp = script.Timestamp
|
||||
}, _jsonOptions);
|
||||
|
||||
await OnChunkReceived(Response, json);
|
||||
}
|
||||
|
||||
await OnEventCompleted(Response);
|
||||
}
|
||||
|
||||
private async Task OnChunkReceived(HttpResponse response, string text)
|
||||
{
|
||||
var buffer = Encoding.UTF8.GetBytes($"data:{text}\n");
|
||||
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
||||
await Task.Delay(10);
|
||||
|
||||
buffer = Encoding.UTF8.GetBytes("\n");
|
||||
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
private async Task OnEventCompleted(HttpResponse response)
|
||||
{
|
||||
var buffer = Encoding.UTF8.GetBytes("data:[DONE]\n");
|
||||
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
||||
|
||||
buffer = Encoding.UTF8.GetBytes("\n");
|
||||
await response.Body.WriteAsync(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
private JsonSerializerOptions InitJsonOptions(BotSharpOptions options)
|
||||
{
|
||||
var jsonOption = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
AllowTrailingCommas = true
|
||||
};
|
||||
|
||||
if (options?.JsonSerializerOptions != null)
|
||||
{
|
||||
foreach (var option in options.JsonSerializerOptions.Converters)
|
||||
{
|
||||
jsonOption.Converters.Add(option);
|
||||
}
|
||||
}
|
||||
|
||||
return jsonOption;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,12 +108,20 @@ public class UserController : ControllerBase
|
|||
{
|
||||
return await _userService.VerifyEmailExisting(email);
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/user/verifycode")]
|
||||
[HttpPost("/user/verifycode-out")]
|
||||
public async Task<bool> SendVerificationCodeResetPassword([FromBody] UserCreationModel user)
|
||||
{
|
||||
return await _userService.SendVerificationCodeResetPassword(user.ToUser());
|
||||
return await _userService.SendVerificationCodeResetPasswordNoLogin(user.ToUser());
|
||||
}
|
||||
|
||||
[HttpPost("/user/verifycode-in")]
|
||||
public async Task<bool> SendVerificationCodeResetPasswordLogined()
|
||||
{
|
||||
return await _userService.SendVerificationCodeResetPasswordLogin();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/user/resetpassword")]
|
||||
public async Task<bool> ResetUserPassword([FromBody] UserResetPasswordModel user)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Anthropic.SDK" Version="3.2.3" />
|
||||
<PackageReference Include="Anthropic.SDK" Version="4.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -179,8 +179,15 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
Model = settings.Name,
|
||||
Stream = false,
|
||||
Temperature = temperature,
|
||||
SystemMessage = instruction,
|
||||
Tools = new List<Function>() { }
|
||||
Tools = new List<Anthropic.SDK.Common.Tool>()
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(instruction))
|
||||
{
|
||||
parameters.System = new List<SystemMessage>()
|
||||
{
|
||||
new SystemMessage(instruction)
|
||||
};
|
||||
};
|
||||
|
||||
JsonSerializerOptions jsonSerializationOptions = new()
|
||||
|
|
@ -221,7 +228,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
private string GetPrompt(MessageParameters parameters)
|
||||
{
|
||||
var prompt = $"{parameters.SystemMessage}\r\n";
|
||||
var prompt = $"{string.Join("\r\n", parameters.System.Select(x => x.Text))}\r\n";
|
||||
prompt += "\r\n[CONVERSATION]";
|
||||
|
||||
var verbose = string.Join("\r\n", parameters.Messages
|
||||
|
|
@ -264,7 +271,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
var functions = string.Join("\r\n", parameters.Tools.Select(x =>
|
||||
{
|
||||
return $"\r\n{x.Name}: {x.Description}\r\n{JsonSerializer.Serialize(x.Parameters)}";
|
||||
return $"\r\n{x.Function.Name}: {x.Function.Description}\r\n{JsonSerializer.Serialize(x.Function.Parameters)}";
|
||||
}));
|
||||
prompt += $"\r\n[FUNCTIONS]\r\n{functions}\r\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public class ChatHubConversationHook : ConversationHookBase
|
|||
private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
|
||||
private const string GENERATE_SENDER_ACTION = "OnSenderActionGenerated";
|
||||
private const string DELETE_MESSAGE = "OnMessageDeleted";
|
||||
private const string GENERATE_NOTIFICATION = "OnNotificationGenerated";
|
||||
#endregion
|
||||
|
||||
public ChatHubConversationHook(
|
||||
|
|
@ -53,6 +54,7 @@ public class ChatHubConversationHook : ConversationHookBase
|
|||
{
|
||||
ConversationId = conv.ConversationId,
|
||||
MessageId = message.MessageId,
|
||||
Payload = message.Payload,
|
||||
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
|
||||
Sender = UserViewModel.FromUser(sender)
|
||||
};
|
||||
|
|
@ -117,6 +119,31 @@ public class ChatHubConversationHook : ConversationHookBase
|
|||
await base.OnResponseGenerated(message);
|
||||
}
|
||||
|
||||
|
||||
public override async Task OnNotificationGenerated(RoleDialogModel message)
|
||||
{
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var json = JsonSerializer.Serialize(new ChatResponseModel()
|
||||
{
|
||||
ConversationId = conv.ConversationId,
|
||||
MessageId = message.MessageId,
|
||||
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
|
||||
Function = message.FunctionName,
|
||||
RichContent = message.SecondaryRichContent ?? message.RichContent,
|
||||
Data = message.Data,
|
||||
Sender = new UserViewModel()
|
||||
{
|
||||
FirstName = "AI",
|
||||
LastName = "Assistant",
|
||||
Role = AgentRole.Assistant
|
||||
}
|
||||
}, _options.JsonSerializerOptions);
|
||||
|
||||
await GenerateNotification(json);
|
||||
await base.OnNotificationGenerated(message);
|
||||
}
|
||||
|
||||
|
||||
public override async Task OnMessageDeleted(string conversationId, string messageId)
|
||||
{
|
||||
var model = new ChatResponseModel
|
||||
|
|
@ -153,5 +180,10 @@ public class ChatHubConversationHook : ConversationHookBase
|
|||
{
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync(DELETE_MESSAGE, model);
|
||||
}
|
||||
|
||||
private async Task GenerateNotification(string? json)
|
||||
{
|
||||
await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_NOTIFICATION, json);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -60,7 +60,20 @@ public partial class KnowledgeService
|
|||
}
|
||||
|
||||
// Save to vector db
|
||||
var dataIds = await SaveToVectorDb(collectionName, fileId, file.FileName, contents, file.FileSource);
|
||||
var payload = new Dictionary<string, object>()
|
||||
{
|
||||
{ KnowledgePayloadName.DataSource, VectorDataSource.File },
|
||||
{ KnowledgePayloadName.FileId, fileId.ToString() },
|
||||
{ KnowledgePayloadName.FileName, file.FileName },
|
||||
{ KnowledgePayloadName.FileSource, file.FileSource }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(file.FileUrl))
|
||||
{
|
||||
payload[KnowledgePayloadName.FileUrl] = file.FileUrl;
|
||||
}
|
||||
|
||||
var dataIds = await SaveToVectorDb(collectionName, contents, payload);
|
||||
if (!dataIds.IsNullOrEmpty())
|
||||
{
|
||||
db.SaveKnolwedgeBaseFileMeta(new KnowledgeDocMetaData
|
||||
|
|
@ -99,7 +112,7 @@ public partial class KnowledgeService
|
|||
|
||||
|
||||
public async Task<bool> ImportDocumentContentToKnowledge(string collectionName, string fileName, string fileSource,
|
||||
IEnumerable<string> contents, DocMetaRefData? refData = null)
|
||||
IEnumerable<string> contents, DocMetaRefData? refData = null, Dictionary<string, object>? payload = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(collectionName)
|
||||
|| string.IsNullOrWhiteSpace(fileName)
|
||||
|
|
@ -119,7 +132,26 @@ public partial class KnowledgeService
|
|||
var fileId = Guid.NewGuid();
|
||||
var contentType = FileUtility.GetFileContentType(fileName);
|
||||
|
||||
var dataIds = await SaveToVectorDb(collectionName, fileId, fileName, contents, fileSource, fileUrl: refData?.Url);
|
||||
var innerPayload = new Dictionary<string, object>();
|
||||
if (payload != null)
|
||||
{
|
||||
foreach (var item in payload)
|
||||
{
|
||||
innerPayload[item.Key] = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
innerPayload[KnowledgePayloadName.DataSource] = VectorDataSource.File;
|
||||
innerPayload[KnowledgePayloadName.FileId] = fileId.ToString();
|
||||
innerPayload[KnowledgePayloadName.FileName] = fileName;
|
||||
innerPayload[KnowledgePayloadName.FileSource] = fileSource;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(refData?.Url))
|
||||
{
|
||||
innerPayload[KnowledgePayloadName.FileUrl] = refData.Url;
|
||||
}
|
||||
|
||||
var dataIds = await SaveToVectorDb(collectionName, contents, innerPayload);
|
||||
db.SaveKnolwedgeBaseFileMeta(new KnowledgeDocMetaData
|
||||
{
|
||||
Collection = collectionName,
|
||||
|
|
@ -385,9 +417,7 @@ public partial class KnowledgeService
|
|||
return saved;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SaveToVectorDb(
|
||||
string collectionName, Guid fileId, string fileName, IEnumerable<string> contents,
|
||||
string fileSource = KnowledgeDocSource.Api, string vectorDataSource = VectorDataSource.File, string? fileUrl = null)
|
||||
private async Task<IEnumerable<string>> SaveToVectorDb(string collectionName, IEnumerable<string> contents, Dictionary<string, object>? payload = null)
|
||||
{
|
||||
if (contents.IsNullOrEmpty())
|
||||
{
|
||||
|
|
@ -398,25 +428,12 @@ public partial class KnowledgeService
|
|||
var vectorDb = GetVectorDb();
|
||||
var textEmbedding = GetTextEmbedding(collectionName);
|
||||
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
{ KnowledgePayloadName.DataSource, vectorDataSource },
|
||||
{ KnowledgePayloadName.FileId, fileId.ToString() },
|
||||
{ KnowledgePayloadName.FileName, fileName },
|
||||
{ KnowledgePayloadName.FileSource, fileSource }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fileUrl))
|
||||
{
|
||||
payload[KnowledgePayloadName.FileUrl] = fileUrl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < contents.Count(); i++)
|
||||
{
|
||||
var content = contents.ElementAt(i);
|
||||
var vector = await textEmbedding.GetVectorAsync(content);
|
||||
var dataId = Guid.NewGuid();
|
||||
var saved = await vectorDb.Upsert(collectionName, dataId, vector, content, payload);
|
||||
var saved = await vectorDb.Upsert(collectionName, dataId, vector, content, payload ?? new Dictionary<string, object>());
|
||||
|
||||
if (!saved) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ public class DialogMetaDataMongoElement
|
|||
public string Role { get; set; }
|
||||
public string AgentId { get; set; }
|
||||
public string MessageId { get; set; }
|
||||
public string MessageType { get; set; }
|
||||
public string? FunctionName { get; set; }
|
||||
public string? SenderId { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
|
@ -64,6 +65,7 @@ public class DialogMetaDataMongoElement
|
|||
Role = meta.Role,
|
||||
AgentId = meta.AgentId,
|
||||
MessageId = meta.MessageId,
|
||||
MessageType = meta.MessageType,
|
||||
FunctionName = meta.FunctionName,
|
||||
SenderId = meta.SenderId,
|
||||
CreateTime = meta.CreateTime,
|
||||
|
|
@ -77,6 +79,7 @@ public class DialogMetaDataMongoElement
|
|||
Role = meta.Role,
|
||||
AgentId = meta.AgentId,
|
||||
MessageId = meta.MessageId,
|
||||
MessageType = meta.MessageType,
|
||||
FunctionName = meta.FunctionName,
|
||||
SenderId = meta.SenderId,
|
||||
CreateTime = meta.CreateTime,
|
||||
|
|
|
|||
|
|
@ -212,42 +212,45 @@ public class QdrantDb : IVectorDb
|
|||
{
|
||||
foreach (var item in payload)
|
||||
{
|
||||
if (item.Value is string str)
|
||||
{
|
||||
point.Payload[item.Key] = str;
|
||||
}
|
||||
else if (item.Value is bool b)
|
||||
var value = item.Value?.ToString();
|
||||
if (value == null) continue;
|
||||
|
||||
if (bool.TryParse(value, out var b))
|
||||
{
|
||||
point.Payload[item.Key] = b;
|
||||
}
|
||||
else if (item.Value is byte int8)
|
||||
else if (byte.TryParse(value, out var int8))
|
||||
{
|
||||
point.Payload[item.Key] = int8;
|
||||
}
|
||||
else if (item.Value is short int16)
|
||||
else if (short.TryParse(value, out var int16))
|
||||
{
|
||||
point.Payload[item.Key] = int16;
|
||||
}
|
||||
else if (item.Value is int int32)
|
||||
else if (int.TryParse(value, out var int32))
|
||||
{
|
||||
point.Payload[item.Key] = int32;
|
||||
}
|
||||
else if (item.Value is long int64)
|
||||
else if (long.TryParse(value, out var int64))
|
||||
{
|
||||
point.Payload[item.Key] = int64;
|
||||
}
|
||||
else if (item.Value is float f32)
|
||||
else if (float.TryParse(value, out var f32))
|
||||
{
|
||||
point.Payload[item.Key] = f32;
|
||||
}
|
||||
else if (item.Value is double f64)
|
||||
else if (double.TryParse(value, out var f64))
|
||||
{
|
||||
point.Payload[item.Key] = f64;
|
||||
}
|
||||
else if (item.Value is DateTime dt)
|
||||
else if (DateTime.TryParse(value, out var dt))
|
||||
{
|
||||
point.Payload[item.Key] = dt.ToUniversalTime().ToString("o");
|
||||
}
|
||||
else
|
||||
{
|
||||
point.Payload[item.Key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
"type": "string",
|
||||
"description": "table name"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"required": [ "sql_statement", "reason", "tables" ]
|
||||
}
|
||||
}
|
||||
|
|
@ -67,9 +67,13 @@ public partial class PlaywrightWebDriver
|
|||
result.ResponseStatusCode = response.Status;
|
||||
if (response.Status == 200)
|
||||
{
|
||||
// Disable this due to performance issue, some page is too large
|
||||
// result.Body = await page.InnerHTMLAsync("body");
|
||||
result.IsSuccess = true;
|
||||
|
||||
// Be careful if page is too large, it will cause performance issue
|
||||
if (args.ReadInnerHTMLAsBody)
|
||||
{
|
||||
result.Body = await page.InnerHTMLAsync("body");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue