Merge pull request #916 from iceljc/features/add-latest-state
Features/add latest state
This commit is contained in:
commit
d94b5cb297
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -108,7 +108,8 @@ public class RoleDialogModel : ITrackableMessage
|
|||
[JsonPropertyName("generated_images")]
|
||||
public List<ImageGeneration> GeneratedImages { get; set; } = new List<ImageGeneration>();
|
||||
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
|
||||
public string RenderedInstruction { get; set; } = string.Empty;
|
||||
|
||||
private RoleDialogModel()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ public interface IInstructHook
|
|||
string SelfId { get; }
|
||||
Task BeforeCompletion(Agent agent, RoleDialogModel message);
|
||||
Task AfterCompletion(Agent agent, InstructResult result);
|
||||
Task OnResponseGenerated(InstructResponseModel response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,9 @@ public class InstructHookBase : IInstructHook
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public virtual async Task OnResponseGenerated(InstructResponseModel response)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace BotSharp.Abstraction.Instructs.Models;
|
||||
|
||||
public class InstructLogFilter : Pagination
|
||||
{
|
||||
public List<string>? AgentIds { get; set; }
|
||||
public List<string>? Providers { get; set; }
|
||||
public List<string>? Models { get; set; }
|
||||
public List<string>? TemplateNames { get; set; }
|
||||
|
||||
public static InstructLogFilter Empty()
|
||||
{
|
||||
return new InstructLogFilter();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
namespace BotSharp.Abstraction.Instructs.Models;
|
||||
|
||||
public class InstructResponseModel
|
||||
{
|
||||
public string? AgentId { get; set; }
|
||||
public string Provider { get; set; } = default!;
|
||||
public string Model { get; set; } = default!;
|
||||
public string? TemplateName { get; set; }
|
||||
public string UserMessage { get; set; } = default!;
|
||||
public string? SystemInstruction { get; set; }
|
||||
public string CompletionText { get; set; } = default!;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Abstraction.Loggers.Models;
|
||||
|
||||
public class InstructionLogModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("agent_id")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? AgentId { get; set; }
|
||||
|
||||
[JsonPropertyName("agent_name")]
|
||||
[JsonIgnore]
|
||||
public string? AgentName { get; set; }
|
||||
|
||||
[JsonPropertyName("provider")]
|
||||
public string Provider { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("model")]
|
||||
public string Model { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("template_name")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? TemplateName { get; set; }
|
||||
|
||||
[JsonPropertyName("user_message")]
|
||||
public string UserMessage { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("system_instruction")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? SystemInstruction { get; set; }
|
||||
|
||||
[JsonPropertyName("completion_text")]
|
||||
public string CompletionText { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("user_id")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? UserId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, string> States { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("states")]
|
||||
public Dictionary<string, JsonDocument> InnerStates { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("created_time")]
|
||||
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ public interface IAudioCompletion
|
|||
{
|
||||
string Provider { get; }
|
||||
|
||||
string Model { get; }
|
||||
|
||||
Task<string> GenerateTextFromAudioAsync(Stream audio, string audioFileName, string? text = null);
|
||||
Task<BinaryData> GenerateAudioFromTextAsync(string text);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ public interface IChatCompletion
|
|||
/// </summary>
|
||||
string Provider { get; }
|
||||
|
||||
string Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set model name, one provider can consume different model or version(s)
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ public interface IImageCompletion
|
|||
/// </summary>
|
||||
string Provider { get; }
|
||||
|
||||
string Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set model name, one provider can consume different model or version(s)
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ public interface IRealTimeCompletion
|
|||
{
|
||||
string Provider { get; }
|
||||
string Model { get; }
|
||||
|
||||
void SetModelName(string model);
|
||||
|
||||
Task Connect(RealtimeHubConnection conn,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ public interface ITextCompletion
|
|||
/// The LLM provider like Microsoft Azure, OpenAI, ClaudAI
|
||||
/// </summary>
|
||||
string Provider { get; }
|
||||
string Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set model name, one provider can consume different model or version(s)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,14 @@ public interface ITextEmbedding
|
|||
/// The Embedding provider like Microsoft Azure, OpenAI, ClaudAI
|
||||
/// </summary>
|
||||
string Provider { get; }
|
||||
string Model { get; }
|
||||
|
||||
void SetModelName(string model);
|
||||
|
||||
|
||||
Task<float[]> GetVectorAsync(string text);
|
||||
Task<List<float[]>> GetVectorsAsync(List<string> texts);
|
||||
void SetModelName(string model);
|
||||
|
||||
void SetDimension(int dimension);
|
||||
int GetDimension();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,3 +13,17 @@ public class KeyValue
|
|||
return $"Key: {Key}, Value: {Value}";
|
||||
}
|
||||
}
|
||||
|
||||
public class KeyValue<T>
|
||||
{
|
||||
[JsonPropertyName("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public T? Value { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Key: {Key}, Value: {Value}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
|
|
@ -171,6 +172,14 @@ public interface IBotSharpRepository : IHaveServiceProvider
|
|||
=> throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region Instruction Log
|
||||
bool SaveInstructionLogs(IEnumerable<InstructionLogModel> logs)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
PagedItems<InstructionLogModel> GetInstructionLogs(InstructLogFilter filter)
|
||||
=> throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region Statistics
|
||||
BotSharpStats? GetGlobalStats(string metric, string dimension, string dimRefVal, DateTime recordTime, StatsInterval interval)
|
||||
=> throw new NotImplementedException();
|
||||
|
|
|
|||
|
|
@ -92,4 +92,20 @@ public static class StringExtensions
|
|||
|
||||
return JsonSerializer.Deserialize<T[]>(text, options);
|
||||
}
|
||||
|
||||
public static bool IsPrimitiveValue(this string value)
|
||||
{
|
||||
return int.TryParse(value, out _) ||
|
||||
long.TryParse(value, out _) ||
|
||||
double.TryParse(value, out _) ||
|
||||
float.TryParse(value, out _) ||
|
||||
bool.TryParse(value, out _) ||
|
||||
char.TryParse(value, out _) ||
|
||||
byte.TryParse(value, out _) ||
|
||||
sbyte.TryParse(value, out _) ||
|
||||
short.TryParse(value, out _) ||
|
||||
ushort.TryParse(value, out _) ||
|
||||
uint.TryParse(value, out _) ||
|
||||
ulong.TryParse(value, out _);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Instructs;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Files.Services;
|
||||
|
|
@ -6,10 +8,11 @@ public partial class FileInstructService
|
|||
{
|
||||
public async Task<string> ReadImages(string? provider, string? model, string text, IEnumerable<InstructFileModel> images, string? agentId = null)
|
||||
{
|
||||
var innerAgentId = agentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider ?? "openai", model: model ?? "gpt-4o", multiModal: true);
|
||||
var message = await completion.GetChatCompletions(new Agent()
|
||||
{
|
||||
Id = agentId ?? Guid.Empty.ToString(),
|
||||
Id = innerAgentId,
|
||||
}, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, text)
|
||||
|
|
@ -17,16 +20,55 @@ public partial class FileInstructService
|
|||
Files = images?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? new List<BotSharpFile>()
|
||||
}
|
||||
});
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = innerAgentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
UserMessage = text,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
|
||||
return message.Content;
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> GenerateImage(string? provider, string? model, string text, string? agentId = null)
|
||||
{
|
||||
var innerAgentId = agentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-3");
|
||||
var message = await completion.GetImageGeneration(new Agent()
|
||||
{
|
||||
Id = agentId ?? Guid.Empty.ToString(),
|
||||
Id = innerAgentId,
|
||||
}, new RoleDialogModel(AgentRole.User, text));
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = innerAgentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
UserMessage = text,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
@ -37,6 +79,7 @@ public partial class FileInstructService
|
|||
throw new ArgumentException($"Cannot find image url or data!");
|
||||
}
|
||||
|
||||
var innerAgentId = agentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
|
||||
var bytes = await DownloadFile(image);
|
||||
using var stream = new MemoryStream();
|
||||
|
|
@ -46,10 +89,29 @@ public partial class FileInstructService
|
|||
var fileName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
|
||||
var message = await completion.GetImageVariation(new Agent()
|
||||
{
|
||||
Id = agentId ?? Guid.Empty.ToString()
|
||||
Id = innerAgentId
|
||||
}, new RoleDialogModel(AgentRole.User, string.Empty), stream, fileName);
|
||||
|
||||
stream.Close();
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = innerAgentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
UserMessage = string.Empty,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
@ -60,6 +122,7 @@ public partial class FileInstructService
|
|||
throw new ArgumentException($"Cannot find image url or data!");
|
||||
}
|
||||
|
||||
var innerAgentId = agentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
|
||||
var bytes = await DownloadFile(image);
|
||||
using var stream = new MemoryStream();
|
||||
|
|
@ -69,10 +132,29 @@ public partial class FileInstructService
|
|||
var fileName = $"{image.FileName ?? "image"}.{image.FileExtension ?? "png"}";
|
||||
var message = await completion.GetImageEdits(new Agent()
|
||||
{
|
||||
Id = agentId ?? Guid.Empty.ToString()
|
||||
Id = innerAgentId
|
||||
}, new RoleDialogModel(AgentRole.User, text), stream, fileName);
|
||||
|
||||
stream.Close();
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = innerAgentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
UserMessage = text,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +166,7 @@ public partial class FileInstructService
|
|||
throw new ArgumentException($"Cannot find image/mask url or data");
|
||||
}
|
||||
|
||||
var innerAgentId = agentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetImageCompletion(_services, provider: provider ?? "openai", model: model ?? "dall-e-2");
|
||||
var imageBytes = await DownloadFile(image);
|
||||
var maskBytes = await DownloadFile(mask);
|
||||
|
|
@ -100,11 +183,30 @@ public partial class FileInstructService
|
|||
var maskName = $"{mask.FileName ?? "mask"}.{mask.FileExtension ?? "png"}";
|
||||
var message = await completion.GetImageEdits(new Agent()
|
||||
{
|
||||
Id = agentId ?? Guid.Empty.ToString()
|
||||
Id = innerAgentId
|
||||
}, new RoleDialogModel(AgentRole.User, text), imageStream, imageName, maskStream, maskName);
|
||||
|
||||
imageStream.Close();
|
||||
maskStream.Close();
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = innerAgentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
UserMessage = text,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using BotSharp.Abstraction.Files.Converters;
|
||||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Instructs;
|
||||
|
||||
namespace BotSharp.Core.Files.Services;
|
||||
|
||||
|
|
@ -23,11 +25,12 @@ public partial class FileInstructService
|
|||
var images = await ConvertPdfToImages(pdfFiles);
|
||||
if (images.IsNullOrEmpty()) return content;
|
||||
|
||||
var innerAgentId = agentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider ?? "openai",
|
||||
model: model, modelId: modelId ?? "gpt-4", multiModal: true);
|
||||
var message = await completion.GetChatCompletions(new Agent()
|
||||
{
|
||||
Id = agentId ?? Guid.Empty.ToString(),
|
||||
Id = innerAgentId,
|
||||
}, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, prompt)
|
||||
|
|
@ -35,6 +38,25 @@ public partial class FileInstructService
|
|||
Files = images.Select(x => new BotSharpFile { FileStorageUrl = x }).ToList()
|
||||
}
|
||||
});
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = innerAgentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
UserMessage = prompt,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
|
||||
return message.Content;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BotSharp.Core.Instructs;
|
||||
|
|
@ -17,7 +18,15 @@ public class InsturctionPlugin : IBotSharpPlugin
|
|||
public bool AttachMenu(List<PluginMenuDef> menu)
|
||||
{
|
||||
var section = menu.First(x => x.Label == "Apps");
|
||||
menu.Add(new PluginMenuDef("Instruction", link: "page/instruction", icon: "bx bx-book-content", weight: section.Weight + 5));
|
||||
menu.Add(new PluginMenuDef("Instruction", icon: "bx bx-book-content", weight: section.Weight + 5)
|
||||
{
|
||||
SubMenu = new List<PluginMenuDef>
|
||||
{
|
||||
new PluginMenuDef("Instruction", link: "page/instruction"),
|
||||
new PluginMenuDef("Log", link: "page/instruction/log") { Roles = [UserRole.Root, UserRole.Admin] }
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ public partial class InstructService
|
|||
}
|
||||
}
|
||||
|
||||
var provider = string.Empty;
|
||||
var model = string.Empty;
|
||||
|
||||
// Render prompt
|
||||
var prompt = string.IsNullOrEmpty(templateName) ?
|
||||
agentService.RenderedInstruction(agent) :
|
||||
|
|
@ -57,11 +60,18 @@ public partial class InstructService
|
|||
};
|
||||
if (completer is ITextCompletion textCompleter)
|
||||
{
|
||||
instruction = null;
|
||||
provider = textCompleter.Provider;
|
||||
model = textCompleter.Model;
|
||||
|
||||
var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId);
|
||||
response.Text = result;
|
||||
}
|
||||
else if (completer is IChatCompletion chatCompleter)
|
||||
{
|
||||
provider = chatCompleter.Provider;
|
||||
model = chatCompleter.Model;
|
||||
|
||||
if (instruction == "#TEMPLATE#")
|
||||
{
|
||||
instruction = prompt;
|
||||
|
|
@ -93,6 +103,16 @@ public partial class InstructService
|
|||
}
|
||||
|
||||
await hook.AfterCompletion(agent, response);
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = agentId,
|
||||
Provider = provider,
|
||||
Model = model,
|
||||
TemplateName = templateName,
|
||||
UserMessage = prompt,
|
||||
SystemInstruction = instruction,
|
||||
CompletionText = response.Text
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -33,13 +33,19 @@ public partial class FileRepository
|
|||
var stateFile = Path.Combine(dir, STATE_FILE);
|
||||
if (!File.Exists(stateFile))
|
||||
{
|
||||
File.WriteAllText(stateFile, JsonSerializer.Serialize(new List<StateKeyValue>(), _options));
|
||||
File.WriteAllText(stateFile, "[]");
|
||||
}
|
||||
|
||||
var latestStateFile = Path.Combine(dir, CONV_LATEST_STATE_FILE);
|
||||
if (!File.Exists(latestStateFile))
|
||||
{
|
||||
File.WriteAllText(latestStateFile, "{}");
|
||||
}
|
||||
|
||||
var breakpointFile = Path.Combine(dir, BREAKPOINT_FILE);
|
||||
if (!File.Exists(breakpointFile))
|
||||
{
|
||||
File.WriteAllText(breakpointFile, JsonSerializer.Serialize(new List<ConversationBreakpoint>(), _options));
|
||||
File.WriteAllText(breakpointFile, "[]");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -300,14 +306,21 @@ public partial class FileRepository
|
|||
if (states.IsNullOrEmpty()) return;
|
||||
|
||||
var convDir = FindConversationDirectory(conversationId);
|
||||
if (!string.IsNullOrEmpty(convDir))
|
||||
if (string.IsNullOrEmpty(convDir)) return;
|
||||
|
||||
var stateFile = Path.Combine(convDir, STATE_FILE);
|
||||
if (File.Exists(stateFile))
|
||||
{
|
||||
var stateFile = Path.Combine(convDir, STATE_FILE);
|
||||
if (File.Exists(stateFile))
|
||||
{
|
||||
var stateStr = JsonSerializer.Serialize(states, _options);
|
||||
File.WriteAllText(stateFile, stateStr);
|
||||
}
|
||||
var stateStr = JsonSerializer.Serialize(states, _options);
|
||||
File.WriteAllText(stateFile, stateStr);
|
||||
}
|
||||
|
||||
var latestStateFile = Path.Combine(convDir, CONV_LATEST_STATE_FILE);
|
||||
if (File.Exists(latestStateFile))
|
||||
{
|
||||
var latestStates = BuildLatestStates(states);
|
||||
var stateStr = JsonSerializer.Serialize(latestStates, _options);
|
||||
File.WriteAllText(latestStateFile, stateStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -427,25 +440,63 @@ public partial class FileRepository
|
|||
}
|
||||
|
||||
// Check states
|
||||
if (filter != null && !filter.States.IsNullOrEmpty())
|
||||
if (matched && filter != null && !filter.States.IsNullOrEmpty())
|
||||
{
|
||||
var stateFile = Path.Combine(d, STATE_FILE);
|
||||
var convStates = CollectConversationStates(stateFile);
|
||||
foreach (var pair in filter.States)
|
||||
var latestStateFile = Path.Combine(d, CONV_LATEST_STATE_FILE);
|
||||
var convStates = CollectConversationLatestStates(latestStateFile);
|
||||
|
||||
if (convStates.IsNullOrEmpty())
|
||||
{
|
||||
if (pair == null || string.IsNullOrWhiteSpace(pair.Key)) continue;
|
||||
|
||||
var foundState = convStates.FirstOrDefault(x => x.Key.IsEqualTo(pair.Key));
|
||||
if (foundState == null)
|
||||
matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var pair in filter.States)
|
||||
{
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
if (pair == null || string.IsNullOrWhiteSpace(pair.Key)) continue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pair.Value))
|
||||
{
|
||||
var curValue = foundState.Values.LastOrDefault()?.Data;
|
||||
matched = matched && pair.Value.IsEqualTo(curValue);
|
||||
var components = pair.Key.Split(".").ToList();
|
||||
var primaryKey = components[0];
|
||||
if (convStates.TryGetValue(primaryKey, out var doc))
|
||||
{
|
||||
var elem = doc.RootElement.GetProperty("data");
|
||||
if (components.Count < 2)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(pair.Value))
|
||||
{
|
||||
if (elem.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
matched = false;
|
||||
}
|
||||
else if (elem.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
matched = elem.EnumerateArray().Where(x => x.ValueKind != JsonValueKind.Null)
|
||||
.Select(x => x.ToString())
|
||||
.Any(x => x == pair.Value);
|
||||
}
|
||||
else if (elem.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
matched = elem.GetString() == pair.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = elem.GetRawText() == pair.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var paths = components.Where((_, idx) => idx > 0);
|
||||
var found = FindState(elem, paths, pair.Value);
|
||||
matched = found != null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = false;
|
||||
}
|
||||
|
||||
if (!matched) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -575,8 +626,9 @@ public partial class FileRepository
|
|||
// Handle truncated states
|
||||
var refTime = dialogs.ElementAt(foundIdx).MetaData.CreatedTime;
|
||||
var stateDir = Path.Combine(convDir, STATE_FILE);
|
||||
var latestStateDir = Path.Combine(convDir, CONV_LATEST_STATE_FILE);
|
||||
var states = CollectConversationStates(stateDir);
|
||||
isSaved = HandleTruncatedStates(stateDir, states, messageId, refTime);
|
||||
isSaved = HandleTruncatedStates(stateDir, latestStateDir, states, messageId, refTime);
|
||||
|
||||
// Handle truncated breakpoints
|
||||
var breakpointDir = Path.Combine(convDir, BREAKPOINT_FILE);
|
||||
|
|
@ -703,7 +755,7 @@ public partial class FileRepository
|
|||
return isSaved;
|
||||
}
|
||||
|
||||
private bool HandleTruncatedStates(string stateDir, List<StateKeyValue> states, string refMsgId, DateTime refTime)
|
||||
private bool HandleTruncatedStates(string stateDir, string latestStateDir, List<StateKeyValue> states, string refMsgId, DateTime refTime)
|
||||
{
|
||||
var truncatedStates = new List<StateKeyValue>();
|
||||
foreach (var state in states)
|
||||
|
|
@ -724,6 +776,10 @@ public partial class FileRepository
|
|||
}
|
||||
|
||||
var isSaved = SaveTruncatedStates(stateDir, truncatedStates);
|
||||
if (isSaved)
|
||||
{
|
||||
SaveTruncatedLatestStates(latestStateDir, truncatedStates);
|
||||
}
|
||||
return isSaved;
|
||||
}
|
||||
|
||||
|
|
@ -794,6 +850,17 @@ public partial class FileRepository
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool SaveTruncatedLatestStates(string latestStateDir, List<StateKeyValue> states)
|
||||
{
|
||||
if (string.IsNullOrEmpty(latestStateDir) || states == null) return false;
|
||||
if (!File.Exists(latestStateDir)) File.Create(latestStateDir);
|
||||
|
||||
var latestStates = BuildLatestStates(states);
|
||||
var stateStr = JsonSerializer.Serialize(latestStates, _options);
|
||||
File.WriteAllText(latestStateDir, stateStr);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SaveTruncatedBreakpoints(string breakpointDir, List<ConversationBreakpoint> breakpoints)
|
||||
{
|
||||
if (string.IsNullOrEmpty(breakpointDir) || breakpoints == null) return false;
|
||||
|
|
@ -804,22 +871,108 @@ public partial class FileRepository
|
|||
return true;
|
||||
}
|
||||
|
||||
private string? EncodeText(string? text)
|
||||
private Dictionary<string, JsonDocument> CollectConversationLatestStates(string latestStateDir)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
if (string.IsNullOrEmpty(latestStateDir) || !File.Exists(latestStateDir)) return [];
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(text);
|
||||
var encoded = Convert.ToBase64String(bytes);
|
||||
return encoded;
|
||||
var str = File.ReadAllText(latestStateDir);
|
||||
var states = JsonSerializer.Deserialize<Dictionary<string, JsonDocument>>(str, _options);
|
||||
return states ?? [];
|
||||
}
|
||||
|
||||
private string? DecodeText(string? text)
|
||||
private Dictionary<string, JsonDocument> BuildLatestStates(List<StateKeyValue> states)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
var endNodes = new Dictionary<string, JsonDocument>();
|
||||
foreach (var pair in states)
|
||||
{
|
||||
var value = pair.Values?.LastOrDefault();
|
||||
if (value == null || !value.Active) continue;
|
||||
|
||||
var decoded = Convert.FromBase64String(text);
|
||||
var origin = Encoding.UTF8.GetString(decoded);
|
||||
return origin;
|
||||
try
|
||||
{
|
||||
var jsonStr = JsonSerializer.Serialize(new { Data = JsonDocument.Parse(value.Data) }, _options);
|
||||
var json = JsonDocument.Parse(jsonStr);
|
||||
endNodes[pair.Key] = json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
var str = JsonSerializer.Serialize(new { Data = value.Data }, _options);
|
||||
var json = JsonDocument.Parse(str);
|
||||
endNodes[pair.Key] = json;
|
||||
}
|
||||
}
|
||||
|
||||
return endNodes;
|
||||
}
|
||||
|
||||
private JsonElement? FindState(JsonElement? root, IEnumerable<string> paths, string? targetValue)
|
||||
{
|
||||
var elem = root;
|
||||
|
||||
if (elem == null || paths.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < paths.Count(); i++)
|
||||
{
|
||||
if (elem == null) return null;
|
||||
|
||||
var field = paths.ElementAt(i);
|
||||
if (elem.Value.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
if (!elem.Value.EnumerateArray().IsNullOrEmpty())
|
||||
{
|
||||
foreach (var item in elem.Value.EnumerateArray())
|
||||
{
|
||||
var subPaths = paths.Where((_, idx) => idx >= i);
|
||||
elem = FindState(item, subPaths, targetValue);
|
||||
if (elem != null)
|
||||
{
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (elem.Value.ValueKind == JsonValueKind.Object && elem.Value.TryGetProperty(field, out var prop))
|
||||
{
|
||||
elem = prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (elem != null && !string.IsNullOrWhiteSpace(targetValue))
|
||||
{
|
||||
if (elem.Value.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (elem.Value.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var isInArray = elem.Value.EnumerateArray().Where(x => x.ValueKind != JsonValueKind.Null)
|
||||
.Select(x => x.ToString())
|
||||
.Any(x => x == targetValue);
|
||||
return isInArray ? elem : null;
|
||||
}
|
||||
else if ((elem.Value.ValueKind == JsonValueKind.String && elem.Value.GetString() == targetValue)
|
||||
|| (elem.Value.ValueKind != JsonValueKind.String && elem.Value.GetRawText() == targetValue))
|
||||
{
|
||||
return elem;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using System.IO;
|
||||
|
||||
|
|
@ -122,6 +123,99 @@ namespace BotSharp.Core.Repository
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Instruction Log
|
||||
public bool SaveInstructionLogs(IEnumerable<InstructionLogModel> logs)
|
||||
{
|
||||
if (logs.IsNullOrEmpty()) return false;
|
||||
|
||||
var baseDir = Path.Combine(_dbSettings.FileRepository, INSTRUCTION_LOG_FOLDER);
|
||||
if (!Directory.Exists(baseDir))
|
||||
{
|
||||
Directory.CreateDirectory(baseDir);
|
||||
}
|
||||
|
||||
foreach (var log in logs)
|
||||
{
|
||||
var file = Path.Combine(baseDir, $"{Guid.NewGuid()}.json");
|
||||
log.InnerStates = BuildLogStates(log.States);
|
||||
var text = JsonSerializer.Serialize(log, _options);
|
||||
File.WriteAllText(file, text);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public PagedItems<InstructionLogModel> GetInstructionLogs(InstructLogFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = InstructLogFilter.Empty();
|
||||
}
|
||||
|
||||
var baseDir = Path.Combine(_dbSettings.FileRepository, INSTRUCTION_LOG_FOLDER);
|
||||
if (!Directory.Exists(baseDir))
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
var logs = new List<InstructionLogModel>();
|
||||
var files = Directory.GetFiles(baseDir);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var json = File.ReadAllText(file);
|
||||
var log = JsonSerializer.Deserialize<InstructionLogModel>(json, _options);
|
||||
if (log == null) continue;
|
||||
|
||||
var matched = true;
|
||||
if (!filter.AgentIds.IsNullOrEmpty())
|
||||
{
|
||||
matched = matched && filter.AgentIds.Contains(log.AgentId);
|
||||
}
|
||||
if (!filter.Providers.IsNullOrEmpty())
|
||||
{
|
||||
matched = matched && filter.Providers.Contains(log.Provider);
|
||||
}
|
||||
if (!filter.Models.IsNullOrEmpty())
|
||||
{
|
||||
matched = matched && filter.Models.Contains(log.Model);
|
||||
}
|
||||
if (!filter.TemplateNames.IsNullOrEmpty())
|
||||
{
|
||||
matched = matched && filter.TemplateNames.Contains(log.TemplateName);
|
||||
}
|
||||
|
||||
if (!matched) continue;
|
||||
|
||||
log.Id = Path.GetFileNameWithoutExtension(file);
|
||||
logs.Add(log);
|
||||
}
|
||||
|
||||
var records = logs.OrderByDescending(x => x.CreatedTime).Skip(filter.Offset).Take(filter.Size);
|
||||
var agentIds = records.Where(x => !string.IsNullOrEmpty(x.AgentId)).Select(x => x.AgentId).ToList();
|
||||
var agents = GetAgents(new AgentFilter
|
||||
{
|
||||
AgentIds = agentIds
|
||||
});
|
||||
|
||||
records = records.Select(x =>
|
||||
{
|
||||
var states = x.InnerStates.ToDictionary(p => p.Key, p =>
|
||||
{
|
||||
var data = p.Value.RootElement.GetProperty("data");
|
||||
return data.ValueKind != JsonValueKind.Null ? data.ToString() : null;
|
||||
});
|
||||
x.AgentName = !string.IsNullOrEmpty(x.AgentId) ? agents.FirstOrDefault(a => a.Id == x.AgentId)?.Name : null;
|
||||
x.States = states ?? [];
|
||||
return x;
|
||||
}).ToList();
|
||||
|
||||
return new PagedItems<InstructionLogModel>
|
||||
{
|
||||
Items = records,
|
||||
Count = logs.Count()
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
private int GetNextLogIndex(string logDir, string id)
|
||||
{
|
||||
|
|
@ -141,6 +235,28 @@ namespace BotSharp.Core.Repository
|
|||
|
||||
return logIndexes.IsNullOrEmpty() ? 0 : logIndexes.Max() + 1;
|
||||
}
|
||||
|
||||
private Dictionary<string, JsonDocument> BuildLogStates(Dictionary<string, string> states)
|
||||
{
|
||||
var dic = new Dictionary<string, JsonDocument>();
|
||||
foreach (var pair in states)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonStr = JsonSerializer.Serialize(new { Data = JsonDocument.Parse(pair.Value) }, _options);
|
||||
var json = JsonDocument.Parse(jsonStr);
|
||||
dic[pair.Key] = json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
var str = JsonSerializer.Serialize(new { Data = pair.Value }, _options);
|
||||
var json = JsonDocument.Parse(str);
|
||||
dic[pair.Key] = json;
|
||||
}
|
||||
}
|
||||
|
||||
return dic;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using BotSharp.Abstraction.Users.Models;
|
|||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Tasks.Models;
|
||||
|
||||
|
||||
namespace BotSharp.Core.Repository;
|
||||
|
||||
public partial class FileRepository : IBotSharpRepository
|
||||
|
|
@ -34,6 +33,7 @@ public partial class FileRepository : IBotSharpRepository
|
|||
private const string DIALOG_FILE = "dialogs.json";
|
||||
private const string STATE_FILE = "state.json";
|
||||
private const string BREAKPOINT_FILE = "breakpoint.json";
|
||||
private const string CONV_LATEST_STATE_FILE = "latest-state.json";
|
||||
private const string TRANSLATION_MEMORY_FILE = "memory.json";
|
||||
|
||||
private const string USERS_FOLDER = "users";
|
||||
|
|
@ -54,6 +54,7 @@ public partial class FileRepository : IBotSharpRepository
|
|||
private const string STATS_FILE = "stats.json";
|
||||
|
||||
private const string CRON_FILE = "cron.json";
|
||||
private const string INSTRUCTION_LOG_FOLDER = "instruction-logs";
|
||||
|
||||
public FileRepository(
|
||||
IServiceProvider services,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public static class BotSharpLoggerExtensions
|
|||
services.AddScoped<IContentGeneratingHook, VerboseLogHook>();
|
||||
services.AddScoped<IContentGeneratingHook, GlobalStatsConversationHook>();
|
||||
services.AddScoped<IConversationHook, RateLimitConversationHook>();
|
||||
services.AddScoped<IInstructHook, InstructionLogHook>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Users;
|
||||
|
||||
namespace BotSharp.Logger.Hooks;
|
||||
|
||||
public class InstructionLogHook : InstructHookBase
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<InstructionLogHook> _logger;
|
||||
private readonly IUserIdentity _user;
|
||||
|
||||
public override string SelfId => string.Empty;
|
||||
|
||||
public InstructionLogHook(
|
||||
IServiceProvider services,
|
||||
ILogger<InstructionLogHook> logger,
|
||||
IUserIdentity user)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
public override async Task OnResponseGenerated(InstructResponseModel response)
|
||||
{
|
||||
if (response == null) return;
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
|
||||
var user = db.GetUserById(_user.Id);
|
||||
db.SaveInstructionLogs(new List<InstructionLogModel>
|
||||
{
|
||||
new InstructionLogModel
|
||||
{
|
||||
AgentId = response.AgentId,
|
||||
Provider = response.Provider,
|
||||
Model = response.Model,
|
||||
TemplateName = response.TemplateName,
|
||||
UserMessage = response.UserMessage,
|
||||
SystemInstruction = response.SystemInstruction,
|
||||
CompletionText = response.CompletionText,
|
||||
States = state.GetStates(),
|
||||
UserId = user?.Id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,5 @@ global using BotSharp.Abstraction.Conversations.Models;
|
|||
global using BotSharp.Abstraction.Conversations;
|
||||
global using BotSharp.Abstraction.Repositories;
|
||||
global using BotSharp.Abstraction.Conversations.Settings;
|
||||
global using BotSharp.Abstraction.Instructs;
|
||||
global using BotSharp.Logger.Hooks;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -52,8 +52,29 @@ public class InstructModeController : ControllerBase
|
|||
.SetState("model", input.Model, source: StateSource.External)
|
||||
.SetState("model_id", input.ModelId, source: StateSource.External);
|
||||
|
||||
var agentId = input.AgentId ?? Guid.Empty.ToString();
|
||||
var textCompletion = CompletionProvider.GetTextCompletion(_services);
|
||||
return await textCompletion.GetCompletion(input.Text, input.AgentId ?? Guid.Empty.ToString(), Guid.NewGuid().ToString());
|
||||
var response = await textCompletion.GetCompletion(input.Text, agentId, Guid.NewGuid().ToString());
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = agentId,
|
||||
Provider = textCompletion.Provider,
|
||||
Model = textCompletion.Model,
|
||||
TemplateName = input.Template,
|
||||
UserMessage = input.Text,
|
||||
CompletionText = response
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
#region Chat
|
||||
|
|
@ -66,15 +87,36 @@ public class InstructModeController : ControllerBase
|
|||
.SetState("model", input.Model, source: StateSource.External)
|
||||
.SetState("model_id", input.ModelId, source: StateSource.External);
|
||||
|
||||
var agentId = input.AgentId ?? Guid.Empty.ToString();
|
||||
var completion = CompletionProvider.GetChatCompletion(_services);
|
||||
var message = await completion.GetChatCompletions(new Agent()
|
||||
{
|
||||
Id = input.AgentId ?? Guid.Empty.ToString(),
|
||||
Id = agentId,
|
||||
Instruction = input.Instruction
|
||||
}, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, input.Text)
|
||||
});
|
||||
|
||||
var hooks = _services.GetServices<IInstructHook>();
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await hook.OnResponseGenerated(new InstructResponseModel
|
||||
{
|
||||
AgentId = agentId,
|
||||
Provider = completion.Provider,
|
||||
Model = completion.Model,
|
||||
TemplateName = input.Template,
|
||||
UserMessage = input.Text,
|
||||
SystemInstruction = message.RenderedInstruction,
|
||||
CompletionText = message.Content
|
||||
});
|
||||
}
|
||||
return message.Content;
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.OpenAPI.ViewModels.Instructs;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace BotSharp.OpenAPI.Controllers;
|
||||
|
|
@ -45,4 +48,16 @@ public class LoggerController : ControllerBase
|
|||
var conversationService = _services.GetRequiredService<IConversationService>();
|
||||
return await conversationService.GetConversationStateLogs(conversationId);
|
||||
}
|
||||
|
||||
[HttpGet("/logger/instruction/log")]
|
||||
public async Task<PagedItems<InstructionLogViewModel>> GetInstructionLogs([FromQuery] InstructLogFilter request)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var logs = db.GetInstructionLogs(request);
|
||||
return new PagedItems<InstructionLogViewModel>
|
||||
{
|
||||
Items = logs.Items.Select(x => InstructionLogViewModel.From(x)),
|
||||
Count = logs.Count
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ public class IncomingInstructRequest : IncomingMessageModel
|
|||
{
|
||||
public string? AgentId { get; set; }
|
||||
public string? Instruction { get; set; }
|
||||
public string? Template { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Instructs;
|
||||
|
||||
public class InstructionLogViewModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("agent_id")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? AgentId { get; set; }
|
||||
|
||||
[JsonPropertyName("agent_name")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? AgentName { get; set; }
|
||||
|
||||
[JsonPropertyName("provider")]
|
||||
public string Provider { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("model")]
|
||||
public string Model { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("template_name")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? TemplateName { get; set; }
|
||||
|
||||
[JsonPropertyName("user_message")]
|
||||
public string UserMessage { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("system_instruction")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? SystemInstruction { get; set; }
|
||||
|
||||
[JsonPropertyName("completion_text")]
|
||||
public string CompletionText { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("user_id")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? UserId { get; set; }
|
||||
|
||||
[JsonPropertyName("states")]
|
||||
public Dictionary<string, string> States { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("created_time")]
|
||||
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public static InstructionLogViewModel From(InstructionLogModel log)
|
||||
{
|
||||
return new InstructionLogViewModel
|
||||
{
|
||||
Id = log.Id,
|
||||
AgentId = log.AgentId,
|
||||
AgentName = log.AgentName,
|
||||
Provider = log.Provider,
|
||||
Model = log.Model,
|
||||
TemplateName = log.TemplateName,
|
||||
UserMessage = log.UserMessage,
|
||||
SystemInstruction = log.SystemInstruction,
|
||||
CompletionText = log.CompletionText,
|
||||
UserId = log.UserId,
|
||||
States = log.States,
|
||||
CreatedTime = log.CreatedTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,12 @@ namespace BotSharp.Plugin.AnthropicAI.Providers;
|
|||
public class ChatCompletionProvider : IChatCompletion
|
||||
{
|
||||
public string Provider => "anthropic";
|
||||
public string Model => _model;
|
||||
|
||||
protected readonly AnthropicSettings _settings;
|
||||
protected readonly IServiceProvider _services;
|
||||
protected readonly ILogger _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
|
||||
protected string _model;
|
||||
|
||||
|
|
@ -57,6 +59,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
ToolCallId = toolResult.Id,
|
||||
FunctionName = toolResult.Name,
|
||||
FunctionArgs = JsonSerializer.Serialize(toolResult.Input),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
else
|
||||
|
|
@ -66,6 +69,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -98,12 +102,15 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
private (string, MessageParameters) PrepareOptions(Agent agent, List<RoleDialogModel> conversations, LlmModelSetting settings)
|
||||
{
|
||||
var instruction = "";
|
||||
renderedInstructions = [];
|
||||
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
|
||||
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
|
||||
{
|
||||
instruction += agentService.RenderedInstruction(agent);
|
||||
var text = agentService.RenderedInstruction(agent);
|
||||
instruction += text;
|
||||
renderedInstructions.Add(text);
|
||||
}
|
||||
|
||||
/*var routing = _services.GetRequiredService<IRoutingService>();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ public class NativeWhisperProvider : IAudioCompletion
|
|||
private readonly ILogger<NativeWhisperProvider> _logger;
|
||||
|
||||
public string Provider => "native-whisper";
|
||||
public string Model => _model;
|
||||
private string _model;
|
||||
|
||||
public NativeWhisperProvider(
|
||||
BotSharpDatabaseSettings dbSettings,
|
||||
|
|
@ -56,11 +58,13 @@ public class NativeWhisperProvider : IAudioCompletion
|
|||
{
|
||||
if (Enum.TryParse(model, true, out GgmlType ggmlType))
|
||||
{
|
||||
_model = model;
|
||||
LoadWhisperModel(ggmlType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Unsupported model type: {model}. Use Tiny model instead!");
|
||||
_model = "Tiny";
|
||||
LoadWhisperModel(GgmlType.Tiny);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ public partial class AudioCompletionProvider : IAudioCompletion
|
|||
private readonly IServiceProvider _services;
|
||||
|
||||
public string Provider => "openai";
|
||||
public string Model => _model;
|
||||
|
||||
private string _model;
|
||||
|
||||
public AudioCompletionProvider(IServiceProvider service)
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
protected readonly AzureOpenAiSettings _settings;
|
||||
protected readonly IServiceProvider _services;
|
||||
protected readonly ILogger<ChatCompletionProvider> _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
|
||||
protected string _model;
|
||||
|
||||
public virtual string Provider => "azure-openai";
|
||||
public string Model => _model;
|
||||
|
||||
public ChatCompletionProvider(
|
||||
AzureOpenAiSettings settings,
|
||||
|
|
@ -58,7 +60,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
FunctionName = toolCall?.FunctionName,
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString()
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -73,6 +76,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -83,6 +87,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -92,6 +97,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +142,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, text)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// After chat completion hook
|
||||
|
|
@ -163,7 +170,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
ToolCallId = toolCall?.Id,
|
||||
FunctionName = toolCall?.FunctionName,
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString()
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -199,7 +207,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var update = choice.ToolCallUpdates?.FirstOrDefault()?.FunctionArgumentsUpdate?.ToString() ?? string.Empty;
|
||||
Console.Write(update);
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update));
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update)
|
||||
{
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +218,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
_logger.LogInformation(choice.ContentUpdate[0]?.Text);
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty));
|
||||
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty)
|
||||
{
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -221,6 +235,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var settingsService = _services.GetRequiredService<ILlmProviderService>();
|
||||
var settings = settingsService.GetSetting(Provider, _model);
|
||||
var allowMultiModal = settings != null && settings.MultiModal;
|
||||
renderedInstructions = [];
|
||||
|
||||
var messages = new List<ChatMessage>();
|
||||
|
||||
|
|
@ -251,6 +266,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
|
||||
{
|
||||
var instruction = agentService.RenderedInstruction(agent);
|
||||
renderedInstructions.Add(instruction);
|
||||
messages.Add(new SystemChatMessage(instruction));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public class TextEmbeddingProvider : ITextEmbedding
|
|||
protected int _dimension;
|
||||
|
||||
public virtual string Provider => "azure-openai";
|
||||
public string Model => _model;
|
||||
|
||||
public TextEmbeddingProvider(
|
||||
AzureOpenAiSettings settings,
|
||||
|
|
@ -49,6 +50,7 @@ public class TextEmbeddingProvider : ITextEmbedding
|
|||
_model = model;
|
||||
}
|
||||
|
||||
|
||||
public void SetDimension(int dimension)
|
||||
{
|
||||
_dimension = dimension > 0 ? dimension : DEFAULT_DIMENSION;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public partial class ImageCompletionProvider : IImageCompletion
|
|||
protected string _model;
|
||||
|
||||
public virtual string Provider => "azure-openai";
|
||||
public string Model => _model;
|
||||
|
||||
public ImageCompletionProvider(
|
||||
AzureOpenAiSettings settings,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public class TextCompletionProvider : ITextCompletion
|
|||
};
|
||||
|
||||
public virtual string Provider => "azure-openai";
|
||||
public string Model => _model;
|
||||
|
||||
public TextCompletionProvider(
|
||||
AzureOpenAiSettings settings,
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
protected readonly IServiceProvider _services;
|
||||
protected readonly ILogger<ChatCompletionProvider> _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
|
||||
protected string _model;
|
||||
public virtual string Provider => "deepseek-ai";
|
||||
public string Model => _model;
|
||||
|
||||
public ChatCompletionProvider(
|
||||
IServiceProvider services,
|
||||
|
|
@ -51,7 +53,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
ToolCallId = toolCall?.Id,
|
||||
FunctionName = toolCall?.FunctionName,
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString()
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -66,6 +69,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +111,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, text)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// After chat completion hook
|
||||
|
|
@ -134,7 +139,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
ToolCallId = toolCall?.Id,
|
||||
FunctionName = toolCall?.FunctionName,
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString()
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -170,7 +176,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var update = choice.ToolCallUpdates?.FirstOrDefault()?.FunctionArgumentsUpdate?.ToString() ?? string.Empty;
|
||||
_logger.LogInformation(update);
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update));
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update)
|
||||
{
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +187,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
_logger.LogInformation(choice.ContentUpdate[0]?.Text);
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty));
|
||||
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty)
|
||||
{
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -197,6 +209,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var settingsService = _services.GetRequiredService<ILlmProviderService>();
|
||||
var settings = settingsService.GetSetting(Provider, _model);
|
||||
var allowMultiModal = settings != null && settings.MultiModal;
|
||||
renderedInstructions = [];
|
||||
|
||||
var messages = new List<ChatMessage>();
|
||||
|
||||
|
|
@ -226,6 +239,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
|
||||
{
|
||||
var text = agentService.RenderedInstruction(agent);
|
||||
renderedInstructions.Add(text);
|
||||
messages.Add(new SystemChatMessage(text));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ public class TextCompletionProvider : ITextCompletion
|
|||
protected string _model;
|
||||
|
||||
public string Provider => "deepseek-ai";
|
||||
public string Model => _model;
|
||||
|
||||
public TextCompletionProvider(
|
||||
IServiceProvider services,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using BotSharp.Abstraction.Agents;
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Loggers;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Mscc.GenerativeAI;
|
||||
|
||||
|
|
@ -12,10 +11,12 @@ public class GeminiChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<GeminiChatCompletionProvider> _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
|
||||
private string _model;
|
||||
|
||||
public string Provider => "google-ai";
|
||||
public string Model => _model;
|
||||
|
||||
public GeminiChatCompletionProvider(
|
||||
IServiceProvider services,
|
||||
|
|
@ -53,7 +54,8 @@ public class GeminiChatCompletionProvider : IChatCompletion
|
|||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
ToolCallId = part.FunctionCall.Name,
|
||||
FunctionName = part.FunctionCall.Name,
|
||||
FunctionArgs = part.FunctionCall.Args?.ToString()
|
||||
FunctionArgs = part.FunctionCall.Args?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
else
|
||||
|
|
@ -62,6 +64,7 @@ public class GeminiChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +101,7 @@ public class GeminiChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var googleSettings = _services.GetRequiredService<GoogleAiSettings>();
|
||||
renderedInstructions = [];
|
||||
|
||||
// Add settings
|
||||
aiModel.UseGoogleSearch = googleSettings.Gemini.UseGoogleSearch;
|
||||
|
|
@ -117,6 +121,7 @@ public class GeminiChatCompletionProvider : IChatCompletion
|
|||
Role = AgentRole.User
|
||||
});
|
||||
|
||||
renderedInstructions.Add(instruction);
|
||||
systemPrompts.Add(instruction);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ public class PalmChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<PalmChatCompletionProvider> _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
|
||||
private string _model;
|
||||
|
||||
public string Provider => "google-palm";
|
||||
public string Model => _model;
|
||||
|
||||
public PalmChatCompletionProvider(
|
||||
IServiceProvider services,
|
||||
|
|
@ -61,7 +63,8 @@ public class PalmChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
FunctionName = llmResponse.FunctionName,
|
||||
FunctionArgs = JsonSerializer.Serialize(llmResponse.Args)
|
||||
FunctionArgs = JsonSerializer.Serialize(llmResponse.Args),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
else
|
||||
|
|
@ -75,7 +78,8 @@ public class PalmChatCompletionProvider : IChatCompletion
|
|||
|
||||
msg = new RoleDialogModel(llmResponse.Role, llmResponse.Content ?? message.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public class GeminiTextCompletionProvider : ITextCompletion
|
|||
private string _model;
|
||||
|
||||
public string Provider => "google-ai";
|
||||
public string Model => _model;
|
||||
|
||||
public GeminiTextCompletionProvider(
|
||||
IServiceProvider services,
|
||||
|
|
@ -74,7 +75,6 @@ public class GeminiTextCompletionProvider : ITextCompletion
|
|||
_model = model;
|
||||
}
|
||||
|
||||
|
||||
private void PrepareOptions(GenerativeModel aiModel)
|
||||
{
|
||||
var settings = _services.GetRequiredService<GoogleAiSettings>();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public class PalmTextCompletionProvider : ITextCompletion
|
|||
private string _model;
|
||||
|
||||
public string Provider => "google-palm";
|
||||
public string Model => _model;
|
||||
|
||||
public PalmTextCompletionProvider(
|
||||
IServiceProvider services,
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ namespace BotSharp.Plugin.HuggingFace.Providers;
|
|||
public class ChatCompletionProvider : IChatCompletion
|
||||
{
|
||||
public string Provider => "huggingface";
|
||||
public string Model => _model;
|
||||
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly HuggingFaceSettings _settings;
|
||||
private readonly ILogger _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
private string _model;
|
||||
|
||||
public ChatCompletionProvider(IServiceProvider services,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
private readonly LlamaSharpSettings _settings;
|
||||
private List<string> renderedInstructions = [];
|
||||
private string _model;
|
||||
|
||||
public ChatCompletionProvider(IServiceProvider services,
|
||||
|
|
@ -20,6 +21,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
}
|
||||
|
||||
public string Provider => "llama-sharp";
|
||||
public string Model => _model;
|
||||
|
||||
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
|
||||
{
|
||||
|
|
@ -64,7 +66,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, totalResponse)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = instruction
|
||||
};
|
||||
|
||||
// After chat completion hook
|
||||
|
|
@ -146,7 +149,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, totalResponse)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = agent.Instruction
|
||||
};
|
||||
|
||||
// Text response received
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ public class TextCompletionProvider : ITextCompletion
|
|||
private readonly ITokenStatistics _tokenStatistics;
|
||||
private string _model;
|
||||
public string Provider => "llama-sharp";
|
||||
public string Model => _model;
|
||||
|
||||
public TextCompletionProvider(IServiceProvider services,
|
||||
ILogger<TextCompletionProvider> logger,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
namespace BotSharp.Plugin.LLamaSharp.Providers;
|
||||
|
||||
|
|
@ -14,6 +12,7 @@ public class TextEmbeddingProvider : ITextEmbedding
|
|||
protected int _dimension = DEFAULT_DIMENSION;
|
||||
|
||||
public string Provider => "llama-sharp";
|
||||
public string Model => string.Empty;
|
||||
|
||||
public TextEmbeddingProvider(IServiceProvider services, LlamaSharpSettings settings)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ namespace BotSharp.Plugin.VertexAI.Providers
|
|||
IServiceProvider services) : IChatCompletion
|
||||
{
|
||||
public string Provider => "vertexai";
|
||||
public string Model => _model;
|
||||
|
||||
private readonly VertexAIConfiguration _config = config;
|
||||
private readonly ChatSettings? _settings = settings;
|
||||
private readonly IServiceProvider _services = services;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ namespace BotSharp.Plugin.VertexAI.Providers
|
|||
IServiceProvider services) : ITextCompletion
|
||||
{
|
||||
public string Provider => "vertexai";
|
||||
public string Model => _model;
|
||||
|
||||
private readonly VertexAIConfiguration _config = config;
|
||||
private readonly ChatSettings? _settings = settings;
|
||||
private readonly IServiceProvider _services = services;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Abstraction.Models;
|
||||
using BotSharp.Plugin.MetaAI.Settings;
|
||||
using FastText.NetWrapper;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -17,6 +18,7 @@ public class fastTextEmbeddingProvider : ITextEmbedding
|
|||
private int _dimension;
|
||||
|
||||
public string Provider => "meta-ai";
|
||||
public string Model => string.Empty;
|
||||
|
||||
public fastTextEmbeddingProvider(IServiceProvider services)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ namespace BotSharp.Plugin.MetaGLM.Providers;
|
|||
public class ChatCompletionProvider : IChatCompletion
|
||||
{
|
||||
public string Provider => "metaglm";
|
||||
public string Model => _model;
|
||||
|
||||
private readonly MetaGLMSettings _settings;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
private readonly MetaGLMClient metaGLMClient;
|
||||
private List<string> renderedInstructions = [];
|
||||
private string _model;
|
||||
|
||||
public ChatCompletionProvider(IServiceProvider services,
|
||||
|
|
@ -49,7 +51,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
responseMessage = new RoleDialogModel(AgentRole.Assistant, response?.choices.FirstOrDefault()?.message.content)
|
||||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.Last().MessageId
|
||||
MessageId = conversations.Last().MessageId,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +64,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.Last().MessageId,
|
||||
FunctionName = toolcall.function.name,
|
||||
FunctionArgs = toolcall.function.arguments
|
||||
FunctionArgs = toolcall.function.arguments,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -87,10 +91,12 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
List<MessageItem> messages = new List<MessageItem>();
|
||||
List<FunctionTool> toolcalls = new List<FunctionTool>();
|
||||
renderedInstructions = [];
|
||||
|
||||
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
|
||||
{
|
||||
var instruction = agentService.RenderedInstruction(agent);
|
||||
renderedInstructions.Add(instruction);
|
||||
messages.Add(new MessageItem("system", instruction));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public sealed class MicrosoftExtensionsAIChatCompletionProvider : IChatCompletio
|
|||
private readonly IChatClient _client;
|
||||
private readonly ILogger<MicrosoftExtensionsAIChatCompletionProvider> _logger;
|
||||
private readonly IServiceProvider _services;
|
||||
private List<string> renderedInstructions = [];
|
||||
private string? _model;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -45,6 +46,7 @@ public sealed class MicrosoftExtensionsAIChatCompletionProvider : IChatCompletio
|
|||
|
||||
/// <inheritdoc/>
|
||||
public string Provider => "microsoft.extensions.ai";
|
||||
public string Model => _model;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetModelName(string model) => _model = model;
|
||||
|
|
@ -54,6 +56,7 @@ public sealed class MicrosoftExtensionsAIChatCompletionProvider : IChatCompletio
|
|||
{
|
||||
// Before chat completion hook
|
||||
var hooks = _services.GetServices<IContentGeneratingHook>().ToArray();
|
||||
renderedInstructions = [];
|
||||
await Task.WhenAll(hooks.Select(hook => hook.BeforeGenerating(agent, conversations)));
|
||||
|
||||
// Configure options
|
||||
|
|
@ -82,6 +85,7 @@ public sealed class MicrosoftExtensionsAIChatCompletionProvider : IChatCompletio
|
|||
if (_services.GetRequiredService<IAgentService>().RenderedInstruction(agent) is string instruction &&
|
||||
instruction.Length > 0)
|
||||
{
|
||||
renderedInstructions.Add(instruction);
|
||||
messages.Add(new(ChatRole.System, instruction));
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +147,8 @@ public sealed class MicrosoftExtensionsAIChatCompletionProvider : IChatCompletio
|
|||
|
||||
RoleDialogModel result = new(AgentRole.Assistant, string.Concat(completion.Message.Contents.OfType<TextContent>()))
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
if (completion.Message.Contents.OfType<FunctionCallContent>().FirstOrDefault() is { } fcc)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public sealed class MicrosoftExtensionsAITextCompletionProvider : ITextCompletio
|
|||
|
||||
/// <inheritdoc/>
|
||||
public string Provider => "microsoft-extensions-ai";
|
||||
public string Model => _model;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the <see cref="MicrosoftExtensionsAITextCompletionProvider"/> class.
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public sealed class MicrosoftExtensionsAITextEmbeddingProvider : ITextEmbedding
|
|||
|
||||
/// <inheritdoc/>
|
||||
public string Provider => "microsoft-extensions-ai";
|
||||
public string Model => _model;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<float[]> GetVectorAsync(string text) =>
|
||||
|
|
|
|||
|
|
@ -14,4 +14,5 @@ public class ConversationDocument : MongoBase
|
|||
public List<string> Tags { get; set; } = [];
|
||||
public DateTime CreatedTime { get; set; }
|
||||
public DateTime UpdatedTime { get; set; }
|
||||
public Dictionary<string, BsonDocument> LatestStates { get; set; } = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
using BotSharp.Abstraction.Loggers.Models;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Collections;
|
||||
|
||||
public class InstructionLogBetaDocument : MongoBase
|
||||
{
|
||||
public string? AgentId { get; set; }
|
||||
public string Provider { get; set; } = default!;
|
||||
public string Model { get; set; } = default!;
|
||||
public string? TemplateName { get; set; }
|
||||
public string UserMessage { get; set; } = default!;
|
||||
public string? SystemInstruction { get; set; }
|
||||
public string CompletionText { get; set; } = default!;
|
||||
public string? UserId { get; set; }
|
||||
public Dictionary<string, BsonDocument> States { get; set; } = new();
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
public static InstructionLogBetaDocument ToMongoModel(InstructionLogModel log)
|
||||
{
|
||||
return new InstructionLogBetaDocument
|
||||
{
|
||||
AgentId = log.AgentId,
|
||||
Provider = log.Provider,
|
||||
Model = log.Model,
|
||||
TemplateName = log.TemplateName,
|
||||
UserMessage = log.UserMessage,
|
||||
SystemInstruction = log.SystemInstruction,
|
||||
CompletionText = log.CompletionText,
|
||||
UserId = log.UserId,
|
||||
CreatedTime = log.CreatedTime
|
||||
};
|
||||
}
|
||||
|
||||
public static InstructionLogModel ToDomainModel(InstructionLogBetaDocument log)
|
||||
{
|
||||
return new InstructionLogModel
|
||||
{
|
||||
Id = log.Id,
|
||||
AgentId = log.AgentId,
|
||||
Provider = log.Provider,
|
||||
Model = log.Model,
|
||||
TemplateName = log.TemplateName,
|
||||
UserMessage = log.UserMessage,
|
||||
SystemInstruction = log.SystemInstruction,
|
||||
CompletionText = log.CompletionText,
|
||||
UserId = log.UserId,
|
||||
CreatedTime = log.CreatedTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -193,4 +193,6 @@ public class MongoDbContext
|
|||
public IMongoCollection<GlobalStatisticsDocument> GlobalStatistics
|
||||
=> GetCollectionOrCreate<GlobalStatisticsDocument>("GlobalStatistics");
|
||||
|
||||
public IMongoCollection<InstructionLogBetaDocument> InstructionLogs
|
||||
=> GetCollectionOrCreate<InstructionLogBetaDocument>("InstructionLogs");
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
||||
|
|
@ -23,7 +24,8 @@ public partial class MongoRepository
|
|||
Status = conversation.Status,
|
||||
Tags = conversation.Tags ?? new(),
|
||||
CreatedTime = utcNow,
|
||||
UpdatedTime = utcNow
|
||||
UpdatedTime = utcNow,
|
||||
LatestStates = []
|
||||
};
|
||||
|
||||
var dialogDoc = new ConversationDialogDocument
|
||||
|
|
@ -72,8 +74,9 @@ public partial class MongoRepository
|
|||
var cronDeleted = _dc.CrontabItems.DeleteMany(conbTabItems);
|
||||
var convDeleted = _dc.Conversations.DeleteMany(filterConv);
|
||||
|
||||
return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0
|
||||
|| contentLogDeleted.DeletedCount > 0 || stateLogDeleted.DeletedCount > 0 || convDeleted.DeletedCount > 0;
|
||||
return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0
|
||||
|| promptLogDeleted.DeletedCount > 0 || contentLogDeleted.DeletedCount > 0
|
||||
|| stateLogDeleted.DeletedCount > 0 || convDeleted.DeletedCount > 0;
|
||||
}
|
||||
|
||||
[SideCar]
|
||||
|
|
@ -266,6 +269,14 @@ public partial class MongoRepository
|
|||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||
|
||||
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
|
||||
|
||||
// Update latest states
|
||||
var endNodes = BuildLatestStates(saveStates);
|
||||
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||
var update = Builders<ConversationDocument>.Update.Set(x => x.LatestStates, endNodes)
|
||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||
|
||||
_dc.Conversations.UpdateOne(filter, update);
|
||||
}
|
||||
|
||||
public void UpdateConversationStatus(string conversationId, string status)
|
||||
|
|
@ -371,21 +382,47 @@ public partial class MongoRepository
|
|||
}
|
||||
|
||||
// Filter states
|
||||
var stateFilters = new List<FilterDefinition<ConversationStateDocument>>();
|
||||
if (filter != null && string.IsNullOrEmpty(filter.Id) && !filter.States.IsNullOrEmpty())
|
||||
{
|
||||
foreach (var pair in filter.States)
|
||||
{
|
||||
var elementFilters = new List<FilterDefinition<StateMongoElement>> { Builders<StateMongoElement>.Filter.Eq(x => x.Key, pair.Key) };
|
||||
if (!string.IsNullOrEmpty(pair.Value))
|
||||
{
|
||||
elementFilters.Add(Builders<StateMongoElement>.Filter.Eq("Values.Data", pair.Value));
|
||||
}
|
||||
stateFilters.Add(Builders<ConversationStateDocument>.Filter.ElemMatch(x => x.States, Builders<StateMongoElement>.Filter.And(elementFilters)));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(pair.Key)) continue;
|
||||
|
||||
var targetConvIds = _dc.ConversationStates.Find(Builders<ConversationStateDocument>.Filter.And(stateFilters)).ToEnumerable().Select(x => x.ConversationId).Distinct().ToList();
|
||||
convFilters.Add(convBuilder.In(x => x.Id, targetConvIds));
|
||||
// Format key
|
||||
var keys = pair.Key.Split(".").ToList();
|
||||
keys.Insert(1, "data");
|
||||
keys.Insert(0, "LatestStates");
|
||||
var formattedKey = string.Join(".", keys);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pair.Value))
|
||||
{
|
||||
convFilters.Add(convBuilder.Exists(formattedKey));
|
||||
}
|
||||
else if (bool.TryParse(pair.Value, out var boolValue))
|
||||
{
|
||||
convFilters.Add(convBuilder.Eq(formattedKey, boolValue));
|
||||
}
|
||||
else if (int.TryParse(pair.Value, out var intValue))
|
||||
{
|
||||
convFilters.Add(convBuilder.Eq(formattedKey, intValue));
|
||||
}
|
||||
else if (decimal.TryParse(pair.Value, out var decimalValue))
|
||||
{
|
||||
convFilters.Add(convBuilder.Eq(formattedKey, decimalValue));
|
||||
}
|
||||
else if (float.TryParse(pair.Value, out var floatValue))
|
||||
{
|
||||
convFilters.Add(convBuilder.Eq(formattedKey, floatValue));
|
||||
}
|
||||
else if (double.TryParse(pair.Value, out var doubleValue))
|
||||
{
|
||||
convFilters.Add(convBuilder.Eq(formattedKey, doubleValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
convFilters.Add(convBuilder.Eq(formattedKey, pair.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and paginate
|
||||
|
|
@ -527,6 +564,7 @@ public partial class MongoRepository
|
|||
var stateFilter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||
var foundStates = _dc.ConversationStates.Find(stateFilter).FirstOrDefault();
|
||||
|
||||
var endNodes = new Dictionary<string, BsonDocument>();
|
||||
if (foundStates != null)
|
||||
{
|
||||
// Truncate states
|
||||
|
|
@ -550,6 +588,7 @@ public partial class MongoRepository
|
|||
truncatedStates.Add(state);
|
||||
}
|
||||
foundStates.States = truncatedStates;
|
||||
endNodes = BuildLatestStates(truncatedStates);
|
||||
}
|
||||
|
||||
// Truncate breakpoints
|
||||
|
|
@ -573,6 +612,7 @@ public partial class MongoRepository
|
|||
// Update conversation
|
||||
var convFilter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
||||
.Set(x => x.LatestStates, endNodes)
|
||||
.Set(x => x.DialogCount, truncatedDialogs.Count);
|
||||
_dc.Conversations.UpdateOne(convFilter, updateConv);
|
||||
|
||||
|
|
@ -621,8 +661,6 @@ public partial class MongoRepository
|
|||
return keys;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private string ConvertSnakeCaseToPascalCase(string snakeCase)
|
||||
{
|
||||
string[] words = snakeCase.Split('_');
|
||||
|
|
@ -640,4 +678,29 @@ public partial class MongoRepository
|
|||
|
||||
return pascalCase.ToString();
|
||||
}
|
||||
|
||||
private Dictionary<string, BsonDocument> BuildLatestStates(List<StateMongoElement> states)
|
||||
{
|
||||
var endNodes = new Dictionary<string, BsonDocument>();
|
||||
foreach (var pair in states)
|
||||
{
|
||||
var value = pair.Values?.LastOrDefault();
|
||||
if (value == null || !value.Active) continue;
|
||||
|
||||
try
|
||||
{
|
||||
var jsonStr = JsonSerializer.Serialize(new { Data = JsonDocument.Parse(value.Data) }, _botSharpOptions.JsonSerializerOptions);
|
||||
var json = BsonDocument.Parse(jsonStr);
|
||||
endNodes[pair.Key] = json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
var str = JsonSerializer.Serialize(new { Data = value.Data }, _botSharpOptions.JsonSerializerOptions);
|
||||
var json = BsonDocument.Parse(str);
|
||||
endNodes[pair.Key] = json;
|
||||
}
|
||||
}
|
||||
|
||||
return endNodes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using BotSharp.Abstraction.Instructs.Models;
|
||||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
||||
|
|
@ -110,4 +113,96 @@ public partial class MongoRepository
|
|||
return logs;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instruction Log
|
||||
public bool SaveInstructionLogs(IEnumerable<InstructionLogModel> logs)
|
||||
{
|
||||
if (logs.IsNullOrEmpty()) return false;
|
||||
|
||||
var docs = new List<InstructionLogBetaDocument>();
|
||||
foreach (var log in logs)
|
||||
{
|
||||
var doc = InstructionLogBetaDocument.ToMongoModel(log);
|
||||
foreach (var pair in log.States)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonStr = JsonSerializer.Serialize(new { Data = JsonDocument.Parse(pair.Value) }, _botSharpOptions.JsonSerializerOptions);
|
||||
var json = BsonDocument.Parse(jsonStr);
|
||||
doc.States[pair.Key] = json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
var jsonStr = JsonSerializer.Serialize(new { Data = pair.Value }, _botSharpOptions.JsonSerializerOptions);
|
||||
var json = BsonDocument.Parse(jsonStr);
|
||||
doc.States[pair.Key] = json;
|
||||
}
|
||||
}
|
||||
docs.Add(doc);
|
||||
}
|
||||
|
||||
_dc.InstructionLogs.InsertMany(docs);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PagedItems<InstructionLogModel> GetInstructionLogs(InstructLogFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = InstructLogFilter.Empty();
|
||||
}
|
||||
|
||||
var builder = Builders<InstructionLogBetaDocument>.Filter;
|
||||
var filters = new List<FilterDefinition<InstructionLogBetaDocument>>() { builder.Empty };
|
||||
|
||||
// Filter logs
|
||||
if (!filter.AgentIds.IsNullOrEmpty())
|
||||
{
|
||||
filters.Add(builder.In(x => x.AgentId, filter.AgentIds));
|
||||
}
|
||||
if (!filter.Providers.IsNullOrEmpty())
|
||||
{
|
||||
filters.Add(builder.In(x => x.Provider, filter.Providers));
|
||||
}
|
||||
if (!filter.Models.IsNullOrEmpty())
|
||||
{
|
||||
filters.Add(builder.In(x => x.Model, filter.Models));
|
||||
}
|
||||
if (!filter.TemplateNames.IsNullOrEmpty())
|
||||
{
|
||||
filters.Add(builder.In(x => x.TemplateName, filter.TemplateNames));
|
||||
}
|
||||
|
||||
var filterDef = builder.And(filters);
|
||||
var sortDef = Builders<InstructionLogBetaDocument>.Sort.Descending(x => x.CreatedTime);
|
||||
var docs = _dc.InstructionLogs.Find(filterDef).Sort(sortDef).Skip(filter.Offset).Limit(filter.Size).ToList();
|
||||
var count = _dc.InstructionLogs.CountDocuments(filterDef);
|
||||
|
||||
var agentIds = docs.Where(x => !string.IsNullOrEmpty(x.AgentId)).Select(x => x.AgentId).ToList();
|
||||
var agents = GetAgents(new AgentFilter
|
||||
{
|
||||
AgentIds = agentIds
|
||||
});
|
||||
|
||||
var logs = docs.Select(x =>
|
||||
{
|
||||
var log = InstructionLogBetaDocument.ToDomainModel(x);
|
||||
log.AgentName = !string.IsNullOrEmpty(x.AgentId) ? agents.FirstOrDefault(a => a.Id == x.AgentId)?.Name : null;
|
||||
log.States = x.States.ToDictionary(p => p.Key, p =>
|
||||
{
|
||||
var jsonStr = p.Value.ToJson();
|
||||
var jsonDoc = JsonDocument.Parse(jsonStr);
|
||||
var data = jsonDoc.RootElement.GetProperty("data");
|
||||
return data.ValueKind != JsonValueKind.Null ? data.ToString() : null;
|
||||
});
|
||||
return log;
|
||||
}).ToList();
|
||||
|
||||
return new PagedItems<InstructionLogModel>
|
||||
{
|
||||
Items = logs,
|
||||
Count = (int)count
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Options;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
|
@ -7,16 +8,19 @@ public partial class MongoRepository : IBotSharpRepository
|
|||
private readonly MongoDbContext _dc;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<MongoRepository> _logger;
|
||||
private readonly BotSharpOptions _botSharpOptions;
|
||||
private UpdateOptions _options;
|
||||
|
||||
public MongoRepository(
|
||||
MongoDbContext dc,
|
||||
IServiceProvider services,
|
||||
ILogger<MongoRepository> logger)
|
||||
ILogger<MongoRepository> logger,
|
||||
BotSharpOptions botSharpOptions)
|
||||
{
|
||||
_dc = dc;
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
_botSharpOptions = botSharpOptions;
|
||||
_options = new UpdateOptions
|
||||
{
|
||||
IsUpsert = true,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ public partial class AudioCompletionProvider : IAudioCompletion
|
|||
private readonly IServiceProvider _services;
|
||||
|
||||
public string Provider => "openai";
|
||||
public string Model => _model;
|
||||
|
||||
private string _model;
|
||||
|
||||
public AudioCompletionProvider(IServiceProvider service)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
protected readonly ILogger<ChatCompletionProvider> _logger;
|
||||
|
||||
protected string _model;
|
||||
private List<string> renderedInstructions = [];
|
||||
|
||||
public virtual string Provider => "openai";
|
||||
public string Model => _model;
|
||||
|
||||
public ChatCompletionProvider(
|
||||
OpenAiSettings settings,
|
||||
|
|
@ -53,7 +55,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
ToolCallId = toolCall?.Id,
|
||||
FunctionName = toolCall?.FunctionName,
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString()
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -68,6 +71,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +116,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, text)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// After chat completion hook
|
||||
|
|
@ -139,7 +144,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
|
||||
ToolCallId = toolCall?.Id,
|
||||
FunctionName = toolCall?.FunctionName,
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString()
|
||||
FunctionArgs = toolCall?.FunctionArguments?.ToString(),
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -175,7 +181,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var update = choice.ToolCallUpdates?.FirstOrDefault()?.FunctionArgumentsUpdate?.ToString() ?? string.Empty;
|
||||
_logger.LogInformation(update);
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update));
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, update)
|
||||
{
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +192,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
_logger.LogInformation(choice.ContentUpdate[0]?.Text);
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty));
|
||||
await onMessageReceived(new RoleDialogModel(choice.Role?.ToString() ?? ChatMessageRole.Assistant.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty)
|
||||
{
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -198,6 +210,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var settingsService = _services.GetRequiredService<ILlmProviderService>();
|
||||
var settings = settingsService.GetSetting(Provider, _model);
|
||||
var allowMultiModal = settings != null && settings.MultiModal;
|
||||
renderedInstructions = [];
|
||||
|
||||
var messages = new List<ChatMessage>();
|
||||
|
||||
|
|
@ -227,6 +240,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
|
||||
{
|
||||
var text = agentService.RenderedInstruction(agent);
|
||||
renderedInstructions.Add(text);
|
||||
messages.Add(new SystemChatMessage(text));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public class TextEmbeddingProvider : ITextEmbedding
|
|||
protected int _dimension = DEFAULT_DIMENSION;
|
||||
|
||||
public virtual string Provider => "openai";
|
||||
public string Model => _model;
|
||||
|
||||
public TextEmbeddingProvider(
|
||||
OpenAiSettings settings,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public partial class ImageCompletionProvider : IImageCompletion
|
|||
protected string _model;
|
||||
|
||||
public virtual string Provider => "openai";
|
||||
public string Model => _model;
|
||||
|
||||
public ImageCompletionProvider(
|
||||
OpenAiSettings settings,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public class TextCompletionProvider : ITextCompletion
|
|||
protected string _model;
|
||||
|
||||
public virtual string Provider => "openai";
|
||||
public string Model => _model;
|
||||
|
||||
public TextCompletionProvider(
|
||||
OpenAiSettings settings,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace BotSharp.Plugin.SemanticKernel
|
|||
|
||||
/// <inheritdoc/>
|
||||
public string Provider => "semantic-kernel";
|
||||
public string Model => _model;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="SemanticKernelChatCompletionProvider"/>
|
||||
|
|
@ -74,7 +75,8 @@ namespace BotSharp.Plugin.SemanticKernel
|
|||
var response = chatMessageContent != null ? chatMessageContent.Content :string.Empty;
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, response)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = instruction
|
||||
};
|
||||
|
||||
// After chat completion hook
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace BotSharp.Plugin.SemanticKernel
|
|||
|
||||
/// <inheritdoc/>
|
||||
public string Provider => "semantic-kernel";
|
||||
public string Model => _model;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="SemanticKernelTextCompletionProvider"/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Abstraction.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.SemanticKernel.Embeddings;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -33,6 +34,7 @@ namespace BotSharp.Plugin.SemanticKernel
|
|||
protected int _dimension;
|
||||
|
||||
public string Provider => "semantic-kernel";
|
||||
public string Model => string.Empty;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<float[]> GetVectorAsync(string text)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ namespace BotSharp.Plugin.SparkDesk.Providers;
|
|||
public class ChatCompletionProvider : IChatCompletion
|
||||
{
|
||||
public string Provider => "sparkdesk";
|
||||
public string Model => _model;
|
||||
|
||||
private readonly SparkDeskSettings _settings;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
private List<string> renderedInstructions = [];
|
||||
private string _model;
|
||||
|
||||
public ChatCompletionProvider(IServiceProvider services,
|
||||
|
|
@ -42,7 +44,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var responseMessage = new RoleDialogModel(AgentRole.Assistant, response.Text)
|
||||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.Last().MessageId
|
||||
MessageId = conversations.Last().MessageId,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
if (response.FunctionCall != null)
|
||||
|
|
@ -52,7 +55,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
CurrentAgentId = agent.Id,
|
||||
MessageId = conversations.Last().MessageId,
|
||||
FunctionName = response.FunctionCall.Name,
|
||||
FunctionArgs = response.FunctionCall.Arguments
|
||||
FunctionArgs = response.FunctionCall.Arguments,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -92,7 +96,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
var msg = new RoleDialogModel(AgentRole.Assistant, response.Text)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// After chat completion hook
|
||||
|
|
@ -116,7 +121,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
FunctionName = response.FunctionCall.Name,
|
||||
FunctionArgs = response.FunctionCall.Arguments
|
||||
FunctionArgs = response.FunctionCall.Arguments,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
};
|
||||
|
||||
// Somethings LLM will generate a function name with agent name.
|
||||
|
|
@ -150,14 +156,16 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
FunctionName = response.FunctionCall.Name,
|
||||
FunctionArgs = response.FunctionCall.Arguments
|
||||
FunctionArgs = response.FunctionCall.Arguments,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, response.Text)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
CurrentAgentId = agent.Id,
|
||||
RenderedInstruction = string.Join("\r\n", renderedInstructions)
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -175,10 +183,12 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var functions = new List<FunctionDef>();
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var messages = new List<ChatMessage>();
|
||||
renderedInstructions = [];
|
||||
|
||||
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
|
||||
{
|
||||
var instruction = agentService.RenderedInstruction(agent);
|
||||
renderedInstructions.Add(instruction);
|
||||
messages.Add(ChatMessage.FromSystem(instruction));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(agent.Knowledges))
|
||||
|
|
|
|||
Loading…
Reference in a new issue