This commit is contained in:
Haiping Chen 2024-06-10 10:51:20 -05:00
commit bf01fbd25f
38 changed files with 1156 additions and 13 deletions

View file

@ -85,6 +85,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.Dashboard",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.SparkDesk", "src\Plugins\BotSharp.Plugin.SparkDesk\BotSharp.Plugin.SparkDesk.csproj", "{289E25C8-63F1-4D52-9909-207724DB40CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MetaGLM", "src\Plugins\BotSharp.Plugin.MetaGLM\BotSharp.Plugin.MetaGLM.csproj", "{CCF745F2-0C95-4ED0-983B-507C528B39EA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.AnthropicAI", "src\Plugins\BotSharp.Plugin.AnthropicAI\BotSharp.Plugin.AnthropicAI.csproj", "{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}"
EndProject
Global
@ -335,6 +337,14 @@ Global
{289E25C8-63F1-4D52-9909-207724DB40CB}.Release|Any CPU.Build.0 = Release|Any CPU
{289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x64.ActiveCfg = Release|Any CPU
{289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x64.Build.0 = Release|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|x64.ActiveCfg = Debug|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|x64.Build.0 = Debug|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|Any CPU.Build.0 = Release|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|x64.ActiveCfg = Release|Any CPU
{CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|x64.Build.0 = Release|Any CPU
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -385,6 +395,7 @@ Global
{D775DB67-A4B4-44E5-9144-522689590057} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
{267998C1-55C2-4ADC-8361-2CDFA5EA6D6C} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
{289E25C8-63F1-4D52-9909-207724DB40CB} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{CCF745F2-0C95-4ED0-983B-507C528B39EA} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

View file

@ -22,11 +22,13 @@ public partial class ConversationService
if (dialogs.IsNullOrEmpty()) continue;
var content = GetConversationContent(dialogs);
if (string.IsNullOrEmpty(content)) continue;
if (string.IsNullOrWhiteSpace(content)) continue;
contents.Add(content);
}
if (contents.IsNullOrEmpty()) return string.Empty;
var router = await agentService.LoadAgent(AIAssistant);
var prompt = GetPrompt(router, contents);
var summary = await Summarize(router, prompt);
@ -39,10 +41,10 @@ public partial class ConversationService
var template = agent.Templates.First(x => x.Name == "conversation.summary").Content;
var render = _services.GetRequiredService<ITemplateRender>();
var texts = string.Empty;
var texts = new List<string>();
for (int i = 0; i < contents.Count; i++)
{
texts += $"[Conversation {i+1}]\r\n{contents[i]}";
texts.Add($"{contents[i]}");
}
return render.Render(template, new Dictionary<string, object>
@ -97,7 +99,12 @@ public partial class ConversationService
foreach (var dialog in dialogs.TakeLast(maxDialogCount))
{
var role = dialog.Role;
if (role != AgentRole.User && role != AgentRole.Assistant) continue;
if (role == AgentRole.Function) continue;
if (role != AgentRole.User)
{
role = AgentRole.Assistant;
}
conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n";
}

View file

@ -138,7 +138,7 @@ public partial class BotSharpFileService
foreach (var messageId in messageIds)
{
var dir = GetConversationFileDirectory(conversationId, messageId);
if (string.IsNullOrEmpty(dir)) continue;
if (!ExistDirectory(dir)) continue;
Thread.Sleep(100);
Directory.Delete(dir, true);

View file

@ -211,7 +211,12 @@ public class UserService : IUserService
{
var db = _services.GetRequiredService<IBotSharpRepository>();
User user = default;
if (_user.UserName != null)
if (_user.Id != null)
{
user = db.GetUserById(_user.Id);
}
else if (_user.UserName != null)
{
user = db.GetUserByUserName(_user.UserName);
}
@ -222,7 +227,7 @@ public class UserService : IUserService
return user;
}
[MemoryCache(10 * 60)]
[MemoryCache(10 * 60, perInstanceCache: true)]
public async Task<User> GetUser(string id)
{
var db = _services.GetRequiredService<IBotSharpRepository>();

View file

@ -1,14 +1,16 @@
Please read each conversation in the [CONVERSATIONS] section and provide a summary.
Please read each conversation in the [CONVERSATIONS] section by the same user and provide a summary.
*** Super Important! Please consider every conversation. Do not only consider the recent sentences. ***
** Please do not respond to the latest conversation.
** If there are different topics in the conversations, please summarize each topic in different sentences and list them in bullets.
* Please use concise sentences to summarize each topic.
* Please do not include excessive details in the summaries.
* Please use 'user' instead of 'you', 'he' or 'she'.
** Please summarize all conversations in one sentence.
** Please do not exceed 900 characters when summarizing the conversations.
* Please do not contain general information in the summary.
* Please include some but not excessive details in the summary.
[CONVERSATIONS]
{% for text in texts -%}
[CONVERSATION]
{{ text }}{{ "\r\n" }}
{%- endfor %}

View file

@ -0,0 +1,40 @@
namespace BotSharp.Plugin.MetaGLM;
public class AuthenticationUtils
{
public static string GenerateToken(string apiKey, int expSeconds)
{
var parts = apiKey.Split('.');
if (parts.Length != 2)
{
throw new ArgumentException("Invalid API key format.");
}
string id = parts[0];
string secret = parts[1];
byte[] keyBytes = Encoding.UTF8.GetBytes(secret);
if (keyBytes.Length < 32)
{
// Extend the key to meet the minimum length requirement
Array.Resize(ref keyBytes, 32);
}
var securityKey = new SymmetricSecurityKey(keyBytes);
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var payload = new JwtPayload
{
{ "api_key", id },
{ "exp", DateTimeOffset.UtcNow.ToUnixTimeSeconds() + expSeconds },
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
};
var header = new JwtHeader(credentials);
header.Add("sign_type", "SIGN");
var token = new JwtSecurityToken(header, payload);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}

View file

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>12.0</LangVersion>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
<OutputPath>$(SolutionDir)packages</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.4.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,31 @@
namespace BotSharp.Plugin.MetaGLM;
public class MessageItemConverter : JsonConverter<MessageItem>
{
public override MessageItem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// Implement deserialization logic if needed
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, MessageItem value, JsonSerializerOptions options)
{
writer.WriteStartObject();
// Serialize all properties except 'content' for ImageToTextMessageItem
foreach (var prop in value.GetType().GetProperties())
{
if (value is ImageToTextMessageItem && prop.Name.Equals("content", StringComparison.OrdinalIgnoreCase))
{
// Skip serializing 'content' for ImageToTextMessageItem
continue;
}
var propValue = prop.GetValue(value);
writer.WritePropertyName(prop.Name);
JsonSerializer.Serialize(writer, propValue, prop.PropertyType, options);
}
writer.WriteEndObject();
}
}

View file

@ -0,0 +1,23 @@
using BotSharp.Plugin.MetaGLM.Modules;
namespace BotSharp.Plugin.MetaGLM
{
public class MetaGLMClient
{
private MetaGLMSettings _settings;
public Chat Chat { get; private set; }
public Images Images { get; private set; }
public Embeddings Embeddings { get; private set; }
public MetaGLMClient(MetaGLMSettings settings)
{
this._settings = settings;
this.Chat = new Chat(this._settings.ApiKey, _settings.BaseAddress);
this.Images = new Images(this._settings.ApiKey, _settings.BaseAddress);
this.Embeddings = new Embeddings(this._settings.ApiKey, _settings.BaseAddress);
}
}
}

View file

@ -0,0 +1,29 @@
namespace BotSharp.Plugin.MetaGLM;
public class MetaGLMPlugin : IBotSharpPlugin
{
public string Id => "35d464d9-dd94-4cac-9e5a-1eaff6b943f5";
public string Name => "MetaGLM AI";
public string Description => "MetaGLM Service including text generation and embedding services.";
public SettingsMeta Settings => new SettingsMeta("MetaGLM");
public object GetNewSettingsInstance()
{
return new MetaGLMSettings();
}
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped(provider =>
{
var settingService = provider.GetRequiredService<ISettingService>();
return settingService.Bind<MetaGLMSettings>("MetaGLM");
});
services.AddScoped<MetaGLMClient>();
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
}
}

View file

@ -0,0 +1,22 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels;
public class EmbeddingRequestBase
{
// "input": input,
// "model": model,
// "encoding_format": encoding_format,
// "user": user,
public string model { get; private set; }
public string input { get; private set; }
public EmbeddingRequestBase SetModel(string model)
{
this.model = model;
return this;
}
public EmbeddingRequestBase SetInput(string input)
{
this.input = input;
return this;
}
}

View file

@ -0,0 +1,29 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels.FunctionModels;
public enum ParameterType
{
String,
Integer,
}
public class FunctionParameterDescriptor
{
public string type { get; set; }
public string description { get; set; }
private static string ToTypeString(ParameterType type)
{
return type switch
{
ParameterType.String => "string",
ParameterType.Integer => "int",
_ => null
};
}
public FunctionParameterDescriptor(ParameterType type, string description)
{
this.type = ToTypeString(type);
this.description = description;
}
}

View file

@ -0,0 +1,26 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels.FunctionModels;
public class FunctionParameters
{
public string type { get; set; }
public Dictionary<string, FunctionParameterDescriptor> properties { get; }
public string[] required { get; set; }
public FunctionParameters()
{
this.type = "object";
this.properties = new Dictionary<string, FunctionParameterDescriptor>();
}
public FunctionParameters AddParameter(string name, ParameterType type, string description)
{
properties[name] = new FunctionParameterDescriptor(type, description);
return this;
}
public FunctionParameters SetRequiredParameter(string[] required)
{
this.required = required;
return this;
}
}

View file

@ -0,0 +1,25 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels.FunctionModels;
public class FunctionTool
{
public string type { get; set; } = "function";
public Dictionary<string, object> function { get; set; } = new();
public FunctionTool SetName(string name)
{
this.function["name"] = name;
return this;
}
public FunctionTool SetDescription(string desc)
{
this.function["description"] = desc;
return this;
}
public FunctionTool SetParameters(FunctionParameters param)
{
this.function["parameters"] = param;
return this;
}
}

View file

@ -0,0 +1,36 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels;
public class ImageRequestBase
{
// "quality": quality,
// "response_format": response_format,
// "size": size,
// "style": style,
// "user": user,
// public string request_id { get; private set; }
public string model { get; private set; }
public string prompt { get; private set; }
// public int n { get; private set; }
// public ImageRequestBase SetRequestId(string requestId)
// {
// this.request_id = requestId;
// return this;
// }
public ImageRequestBase SetModel(string model)
{
this.model = model;
return this;
}
public ImageRequestBase SetPrompt(string prompt)
{
this.prompt = prompt;
return this;
}
// public ImageRequestBase SetN(int n)
// {
// this.n = n;
// return this;
// }
}

View file

@ -0,0 +1,27 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels.ImageToTextModels;
public class ContentType
{
public string type { get; set; }
public string text { set; get; }
public ImageUrlType image_url { set; get; }
public ContentType setType(string type)
{
this.type = type;
return this;
}
public ContentType setText(string text)
{
this.text = text;
return this;
}
public ContentType setImageUrl(string image_url)
{
this.image_url = new ImageUrlType(image_url);
return this;
}
}

View file

@ -0,0 +1,19 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels.ImageToTextModels;
public class ImageToTextMessageItem(string role) : MessageItem(role, null)
{
public ContentType[] content { get; set; } = new ContentType[2];
public ImageToTextMessageItem setText(string text)
{
this.content[0] = new ContentType().setType("text").setText(text);
return this;
}
public ImageToTextMessageItem setImageUrl(string image_url)
{
this.content[1] = new ContentType().setType("Image_url").setImageUrl(image_url);
return this;
}
}

View file

@ -0,0 +1,12 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels.ImageToTextModels
{
public class ImageUrlType
{
public string url { get; set; }
public ImageUrlType(string url)
{
this.url = url;
}
}
}

View file

@ -0,0 +1,17 @@
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels;
public class MessageItem
{
public string role { get; set; }
public string content { get; set; }
public MessageItem(string role, string content)
{
this.role = role;
this.content = content;
}
public MessageItem()
{
}
}

View file

@ -0,0 +1,73 @@
using BotSharp.Plugin.MetaGLM.Models.RequestModels.FunctionModels;
namespace BotSharp.Plugin.MetaGLM.Models.RequestModels
{
public class TextRequestBase
{
public string request_id { get; private set; }
public string model { get; private set; }
public MessageItem[] messages { get; private set; }
public FunctionTool[] tools { get; private set; }
public string tool_choice { get; private set; }
public double top_p { get; private set; }
public double temperature { get; private set; }
public bool stream { get; set; }
public TextRequestBase()
{
this.stream = true;
}
public TextRequestBase SetRequestId(string requestId)
{
this.request_id = requestId;
return this;
}
public TextRequestBase SetModel(string model)
{
this.model = model;
return this;
}
public TextRequestBase SetMessages(MessageItem[] messages)
{
this.messages = messages;
return this;
}
public TextRequestBase SetTools(FunctionTool[] tools)
{
this.tools = tools;
return this;
}
public TextRequestBase SetToolChoice(string toolChoice)
{
this.tool_choice = toolChoice;
return this;
}
public TextRequestBase SetTopP(double topP)
{
if (topP is <= 0.0 or >= 1.0)
{
topP = 0.1;
}
this.top_p = topP;
return this;
}
public TextRequestBase SetTemperature(double temperature)
{
if (temperature is <= 0.0 or >= 1.0)
{
temperature = 0.1;
}
this.temperature = temperature;
return this;
}
}
}

View file

@ -0,0 +1,8 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels.EmbeddingModels;
public class EmbeddingDataItem
{
public int index { get; set; }
public string _object { get; set; }
public double[] embedding { get; set; }
}

View file

@ -0,0 +1,15 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels.EmbeddingModels;
public class EmbeddingResponseBase
{
public string model { set; get; }
public string _object { set; get; }
public Dictionary<string, int> usage { set; get; }
public EmbeddingDataItem[] data { get; set; }
public Dictionary<string, string> error { get; set; }
public static EmbeddingResponseBase FromJson(string json)
{
return JsonSerializer.Deserialize<EmbeddingResponseBase>(json);
}
}

View file

@ -0,0 +1,13 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels.ImageGenerationModels;
public class ImageResponseBase
{
public long created { get; set; }
public List<ImageResponseDataItem> data { get; set; }
public Dictionary<string, string> error { get; set; }
public static ImageResponseBase FromJson(string json)
{
return JsonSerializer.Deserialize<ImageResponseBase>(json);
}
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels.ImageGenerationModels;
public class ImageResponseDataItem
{
public string url { get; set; }
}

View file

@ -0,0 +1,24 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels;
public class ResponseBase
{
public string id { get; set; }
public string request_id { get; set; }
public long created { get; set; }
public string model { get; set; }
public Dictionary<string, int> usage { get; set; }
public ResponseChoiceItem[] choices { get; set; }
public Dictionary<string, string> error { get; set; }
public static ResponseBase FromJson(string json)
{
try
{
return JsonSerializer.Deserialize<ResponseBase>(json);
}
catch (JsonException)
{
return null;
}
}
}

View file

@ -0,0 +1,8 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels;
public class ResponseChoiceDelta
{
public string role { get; set; }
public string content { get; set; }
public ToolCallItem[] tool_calls { get; set; }
}

View file

@ -0,0 +1,9 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels;
public class ResponseChoiceItem
{
public string finish_reason { get; set; }
public int index { get; set; }
public ResponseChoiceDelta message { get; set; }
public ResponseChoiceDelta delta { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels.ToolModels;
public class FunctionDescriptor
{
public string name { get; set; }
public string arguments { get; set; }
}

View file

@ -0,0 +1,9 @@
namespace BotSharp.Plugin.MetaGLM.Models.ResponseModels.ToolModels;
public class ToolCallItem
{
public string id { get; set; }
public FunctionDescriptor function { get; set; }
public int index { get; set; }
public string type { get; set; }
}

View file

@ -0,0 +1,129 @@
namespace BotSharp.Plugin.MetaGLM.Modules;
//public enum ModelPortal
//{
// Regular,
// Character,
//}
public class Chat
{
private string _apiKey;
private string _baseAddress;
private static readonly int API_TOKEN_TTL_SECONDS = 60 * 5;
private static readonly HttpClient client = new();
//private static readonly Dictionary<ModelPortal, string> PORTAL_URLS = new()
//{
// { ModelPortal.Regular , "https://open.bigmodel.cn/api/paas/v4/chat/completions"},
//};
private static readonly JsonSerializerOptions JsonOptions = new ()
{
Converters =
{
new MessageItemConverter()
},
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
public Chat(string apiKey, string basicAddress = "https://open.bigmodel.cn/api/paas/v4/")
{
this._apiKey = apiKey;
this._baseAddress = basicAddress.TrimEnd('/');
}
private async IAsyncEnumerable<string> CompletionBase(TextRequestBase textRequestBody,string apiKey)
{
var json = JsonSerializer.Serialize(textRequestBody, JsonOptions);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var api_key = AuthenticationUtils.GenerateToken(apiKey, API_TOKEN_TTL_SECONDS);
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{_baseAddress}/chat/completions"),
Content = data,
Headers =
{
{ "Authorization", api_key }
},
};
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var stream = await response.Content.ReadAsStreamAsync();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
yield return Encoding.UTF8.GetString(buffer, 0, bytesRead);
}
}
public async Task<ResponseBase> Completion(TextRequestBase textRequestBody)
{
textRequestBody.stream = false;
var sb = new StringBuilder();
await foreach (var str in CompletionBase(textRequestBody, _apiKey))
{
sb.Append(str);
}
return ResponseBase.FromJson(sb.ToString());
}
public async IAsyncEnumerable<ResponseBase> Stream(TextRequestBase textRequestBody )
{
textRequestBody.stream = true;
var buffer = string.Empty;
await foreach (var chunk in CompletionBase(textRequestBody, _apiKey))
{
buffer += chunk;
while (true)
{
int startPos = buffer.IndexOf("data: ", StringComparison.Ordinal);
if (startPos == -1)
{
break;
}
int endPos = buffer.IndexOf("\n\n", startPos, StringComparison.Ordinal);
if (endPos == -1)
{
break;
}
startPos += "data: ".Length;
string jsonString = buffer.Substring(startPos, endPos - startPos);
if (jsonString.Equals("[DONE]"))
{
break;
}
var response = ResponseBase.FromJson(jsonString);
if (response != null)
{
yield return response;
}
buffer = buffer.Substring(endPos + "\n\n".Length);
}
}
var finalResponse = ResponseBase.FromJson(buffer.Trim());
if (finalResponse != null)
{
yield return finalResponse;
}
}
}

View file

@ -0,0 +1,60 @@
namespace BotSharp.Plugin.MetaGLM.Modules;
public class Embeddings
{
private string _apiKey;
private string _baseAddress;
private static readonly int API_TOKEN_TTL_SECONDS = 60 * 5;
static readonly HttpClient client = new();
public Embeddings(string apiKey, string basicAddress = "https://open.bigmodel.cn/api/paas/v4/")
{
this._apiKey = apiKey;
this._baseAddress = basicAddress.TrimEnd('/');
}
private IEnumerable<string> ProcessBase(EmbeddingRequestBase requestBody)
{
var json = JsonSerializer.Serialize(requestBody);
// Console.WriteLine(JsonSerializer.Serialize(requestBody));
// Console.WriteLine("----1----");
// Console.WriteLine(json);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var api_key = AuthenticationUtils.GenerateToken(_apiKey, API_TOKEN_TTL_SECONDS);
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{_baseAddress}/embeddings"),
Content = data,
Headers =
{
{ "Authorization", api_key }
},
};
var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var stream = response.Content.ReadAsStreamAsync().Result;
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
yield return Encoding.UTF8.GetString(buffer, 0, bytesRead);
}
}
public EmbeddingResponseBase Process(EmbeddingRequestBase requestBody)
{
var sb = new StringBuilder();
foreach (var str in ProcessBase(requestBody))
{
sb.Append(str);
}
return EmbeddingResponseBase.FromJson(sb.ToString());
}
}

View file

@ -0,0 +1,55 @@
namespace BotSharp.Plugin.MetaGLM.Modules;
public class Images
{
private string _apiKey;
private string _baseAddress;
private static readonly int API_TOKEN_TTL_SECONDS = 60 * 5;
static readonly HttpClient client = new HttpClient();
public Images(string apiKey, string basicAddress = "https://open.bigmodel.cn/api/paas/v4/")
{
this._apiKey = apiKey;
this._baseAddress = basicAddress.TrimEnd('/');
}
private IEnumerable<string> GenerateBase(ImageRequestBase requestBody)
{
var json = JsonSerializer.Serialize(requestBody);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var api_key = AuthenticationUtils.GenerateToken(_apiKey, API_TOKEN_TTL_SECONDS);
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{_baseAddress}images/generations"),
Content = data,
Headers =
{
{ "Authorization", api_key }
},
};
var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var stream = response.Content.ReadAsStreamAsync().Result;
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
yield return Encoding.UTF8.GetString(buffer, 0, bytesRead);
}
}
public ImageResponseBase Generation(ImageRequestBase requestBody, string apiKey)
{
StringBuilder sb = new StringBuilder();
foreach (var str in GenerateBase(requestBody))
{
sb.Append(str);
}
return ImageResponseBase.FromJson(sb.ToString());
}
}

View file

@ -0,0 +1,238 @@
namespace BotSharp.Plugin.MetaGLM.Providers;
public class ChatCompletionProvider : IChatCompletion
{
public string Provider => "metaglm";
private readonly MetaGLMSettings _settings;
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private readonly MetaGLMClient metaGLMClient;
private string _model;
public ChatCompletionProvider(IServiceProvider services,
MetaGLMSettings settings,
MetaGLMClient client,
ILogger<ChatCompletionProvider> logger)
{
_services = services;
_settings = settings;
_logger = logger;
metaGLMClient = client;
_model = "glm-4";
}
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
{
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
// Before chat completion hook
foreach (var hook in contentHooks)
{
await hook.BeforeGenerating(agent, conversations);
}
var dto = new TextRequestBase();
dto.SetRequestId(Guid.NewGuid().ToString());
dto.SetModel(_settings.ModelId);
dto.SetTemperature(_settings.Temperature);
dto.SetTopP(_settings.TopP);
var prompt = PrepareOptions(agent, conversations, dto);
var response = await metaGLMClient.Chat.Completion(dto);
RoleDialogModel? responseMessage = null;
if (response?.choices.FirstOrDefault()?.finish_reason == "stop")
{
responseMessage = new RoleDialogModel(AgentRole.Assistant, response?.choices.FirstOrDefault()?.message.content)
{
CurrentAgentId = agent.Id,
MessageId = conversations.Last().MessageId
};
}
if (response?.choices.FirstOrDefault()?.finish_reason == "tool_calls")
{
var toolcall = response.choices.FirstOrDefault()?.message.tool_calls.FirstOrDefault();
responseMessage = new RoleDialogModel(AgentRole.Function, JsonSerializer.Serialize(toolcall.function))
{
CurrentAgentId = agent.Id,
MessageId = conversations.Last().MessageId,
FunctionName = toolcall.function.name,
FunctionArgs = toolcall.function.arguments
};
}
// After chat completion hook
foreach (var hook in contentHooks)
{
await hook.AfterGenerated(message: responseMessage, new TokenStatsModel
{
Prompt = prompt,
Provider = Provider,
Model = _model,
PromptCount = response.usage.GetValueOrDefault("prompt_tokens"),
CompletionCount = response.usage.GetValueOrDefault("completion_tokens")
});
}
return responseMessage;
}
private string PrepareOptions(Agent agent, List<RoleDialogModel> conversations, TextRequestBase dto)
{
var agentService = _services.GetRequiredService<IAgentService>();
List<MessageItem> messages = new List<MessageItem>();
List<FunctionTool> toolcalls = new List<FunctionTool>();
if (!string.IsNullOrEmpty(agent.Instruction))
{
var instruction = agentService.RenderedInstruction(agent);
messages.Add(new MessageItem("system", instruction));
}
if (!string.IsNullOrEmpty(agent.Knowledges))
{
messages.Add(new MessageItem("system", agent.Knowledges));
}
var samples = ProviderHelper.GetChatSamples(agent.Samples);
foreach (var message in samples)
{
messages.Add(message.Role == AgentRole.User ?
new MessageItem("user", message.Content) :
new MessageItem("assistant", message.Content));
}
foreach (var function in agent.Functions)
{
var functionTool = ConvertToFunctionTool(function);
toolcalls.Add(functionTool);
}
foreach (var message in conversations)
{
if (message.Role == "function")
{
//messages.Add(ChatMessage.FromUser($"function call result: {message.content}"));
}
else if (message.Role == "user")
{
var userMessage = new MessageItem("user",message.Content);
messages.Add(userMessage);
}
else if (message.Role == "assistant")
{
messages.Add(new MessageItem("assistant", message.Content));
}
}
if (toolcalls.Count > 0)
{
dto.SetTools(toolcalls.ToArray());
dto.SetToolChoice("auto");
}
dto.SetMessages(messages.ToArray());
var prompt = GetPrompt(messages, toolcalls);
//var state = _services.GetRequiredService<IConversationStateService>();
//var temperature = float.Parse(state.GetState("temperature", "0.0"));
//var samplingFactor = float.Parse(state.GetState("sampling_factor", "0.0"));
//dto.SetTemperature(temperature);
return prompt;
}
private FunctionTool ConvertToFunctionTool(FunctionDef def)
{
var functionTool = new FunctionTool()
{
type = "function"
}
.SetName(def.Name)
.SetDescription(def.Description);
var funcParameter = new FunctionParameters() { type = def.Parameters.Type };
funcParameter.SetRequiredParameter(def.Parameters.Required.ToArray());
var parameters = def.Parameters;
var funcParamsProperties = parameters.Properties;
if (funcParamsProperties != null)
{
var props = funcParamsProperties.RootElement.EnumerateObject();
while (props.MoveNext())
{
var prop = props.Current;
var name = prop.Name;
string typestr = prop.Value.GetProperty("type").GetRawText();
if (!string.IsNullOrEmpty(typestr))
{
ParameterType parameterType;
if (Enum.TryParse(typestr, out parameterType))
{
funcParameter.AddParameter(name, parameterType, prop.Value.GetProperty("description").GetRawText());
}
else
{
}
}
}
}
functionTool.SetParameters(funcParameter);
return functionTool;
}
private string GetPrompt(List<MessageItem> messages, List<FunctionTool> functions)
{
var prompt = string.Empty;
if (messages.Count > 0)
{
// System instruction
var verbose = string.Join("\r\n", messages
.Where(x => x.role == AgentRole.System)
.Select(x =>
{
return $"{x.role}: {x.content}";
}));
prompt += $"{verbose}\r\n";
verbose = string.Join("\r\n", messages
.Where(x => x.role != AgentRole.System).Select(x =>
{
return
$"{x.role}: {x.content}";
}));
prompt += $"\r\n{verbose}\r\n";
}
if(functions.Count > 0)
{
}
return prompt;
}
public Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting)
{
throw new NotImplementedException();
}
public Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
{
throw new NotImplementedException();
}
public void SetModelName(string model)
{
_model = model;
}
}

View file

@ -0,0 +1,38 @@
namespace BotSharp.Plugin.MetaGLM.Providers;
internal class ProviderHelper
{
public static string GetClient(string model, IServiceProvider services)
{
var settingsService = services.GetRequiredService<ILlmProviderService>();
var settings = settingsService.GetSetting("metaglm", model);
return settings.ApiKey;
}
public static List<RoleDialogModel> GetChatSamples(List<string> lines)
{
var samples = new List<RoleDialogModel>();
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
if (string.IsNullOrEmpty(line.Trim()))
{
continue;
}
var role = line.Substring(0, line.IndexOf(' ') - 1).Trim();
var content = line.Substring(line.IndexOf(' ') + 1).Trim();
// comments
if (role == "##")
{
continue;
}
samples.Add(new RoleDialogModel(role, content));
}
return samples;
}
}

View file

@ -0,0 +1,17 @@
namespace BotSharp.Plugin.MetaGLM.Settings;
public class MetaGLMSettings
{
/// <summary>
/// In the new mechanism, the API Key issued by the platform contains both “user identifier id” and “signature key secret” in the format of {id}.{secret}.
/// </summary>
public string ApiKey { get; set; }
public string BaseAddress { get; set; } = "https://open.bigmodel.cn/api/paas/v4/";
public string ModelId { get; set; }
public double Temperature { get; set; }
public double TopP { get; set; }
}

View file

@ -0,0 +1,31 @@
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Functions.Models;
global using BotSharp.Abstraction.Loggers;
global using BotSharp.Abstraction.MLTasks;
global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Settings;
global using BotSharp.Plugin.MetaGLM.Models.RequestModels;
global using BotSharp.Plugin.MetaGLM.Models.RequestModels.FunctionModels;
global using BotSharp.Plugin.MetaGLM.Models.RequestModels.ImageToTextModels;
global using BotSharp.Plugin.MetaGLM.Models.ResponseModels;
global using BotSharp.Plugin.MetaGLM.Models.ResponseModels.EmbeddingModels;
global using BotSharp.Plugin.MetaGLM.Models.ResponseModels.ImageGenerationModels;
global using BotSharp.Plugin.MetaGLM.Models.ResponseModels.ToolModels;
global using BotSharp.Plugin.MetaGLM.Providers;
global using BotSharp.Plugin.MetaGLM.Settings;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using Microsoft.IdentityModel.Tokens;
global using System;
global using System.Collections.Generic;
global using System.IdentityModel.Tokens.Jwt;
global using System.Linq;
global using System.Net.Http;
global using System.Text;
global using System.Text.Json;
global using System.Text.Json.Serialization;
global using System.Threading.Tasks;

View file

@ -27,6 +27,9 @@
<ItemGroup>
<ProjectReference Include="..\..\tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.Dashboard\BotSharp.Plugin.Dashboard.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.MetaGLM\BotSharp.Plugin.MetaGLM.csproj" />
<ProjectReference Include="..\Plugins\BotSharp.Plugin.SparkDesk\BotSharp.Plugin.SparkDesk.csproj" />
</ItemGroup>
<ItemGroup Condition="$(SolutionName)==BotSharp">

View file

@ -97,6 +97,17 @@
"CompletionCost": 0.002
}
]
},
{
"Provider": "metaglm",
"Models": [
{
"Name": "chatglm3_6b",
"Type": "chat",
"PromptCost": 0.0015,
"CompletionCost": 0.002
}
]
}
],
@ -239,6 +250,13 @@
"ApiSecret": "",
"ModelVersion": "V3_5"
},
"MetaGLM": {
"ApiKey": "6b6c8b3fca3e5da21d633e350980744d.938gruOqrK4BDqW8",
"BaseAddress": "http://localhost:8100/v1/",
"ModelId": "chatglm3_6b",
"Temperature": 0.7,
"TopP": 0.7
},
"GoogleApi": {
"ApiKey": "",
@ -273,7 +291,8 @@
"BotSharp.Plugin.PizzaBot",
"BotSharp.Plugin.WebDriver",
"BotSharp.Plugin.LLamaSharp",
"BotSharp.Plugin.SparkDesk"
"BotSharp.Plugin.SparkDesk",
"BotSharp.Plugin.MetaGLM"
]
}
}