mirror of
https://github.com/AIDotNet/AntSK.git
synced 2026-02-21 17:09:20 +08:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eef943458e | ||
|
|
f5c80689d4 | ||
|
|
5eaee3130a | ||
|
|
5846473f28 | ||
|
|
94c019b484 | ||
|
|
7e1140c022 | ||
|
|
ea9044719a | ||
|
|
8a96095448 | ||
|
|
fcc8f8751b | ||
|
|
af09ae7c3e | ||
|
|
e8e6a36d7b | ||
|
|
4f89d54ef0 | ||
|
|
2f9e2fb114 | ||
|
|
b6098024b8 | ||
|
|
1700131066 | ||
|
|
189536471a | ||
|
|
f534e0bcc3 | ||
|
|
e203a18e92 | ||
|
|
575a69bf4d | ||
|
|
69fd3a0367 | ||
|
|
8f7e70298e | ||
|
|
0fa3f5a554 | ||
|
|
f420012752 | ||
|
|
c1ca916549 | ||
|
|
dfcf2bdc85 | ||
|
|
72e7acfb7d | ||
|
|
9d06c127dc | ||
|
|
0460b388ab | ||
|
|
45b84ae898 | ||
|
|
41b1cb6f2d | ||
|
|
159aaab38e | ||
|
|
dc351238f6 | ||
|
|
e6491b39c6 | ||
|
|
91b4ed8940 | ||
|
|
ab99098afd | ||
|
|
d14ce2faa0 | ||
|
|
ca293691a8 | ||
|
|
cf8955b9b6 | ||
|
|
512828fdc9 | ||
|
|
91299a96e7 | ||
|
|
4876d9e727 | ||
|
|
a856f2a0e3 | ||
|
|
0e8113e7b0 | ||
|
|
34a953589d | ||
|
|
504ea5a238 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -340,3 +340,4 @@ ASALocalRun/
|
||||
/src/AntSK/AntSK.db
|
||||
/src/AntSK/appsettings.Development.json
|
||||
/src/AntSK.db
|
||||
/src/AntSK/llama_models
|
||||
|
||||
23
README.md
23
README.md
@@ -10,15 +10,17 @@
|
||||
|
||||
- **知识库**:通过文档(Word、PDF、Excel、Txt、Markdown、Json、PPT)等形式导入知识库,可以进行知识库文档。
|
||||
|
||||
- **API插件系统**:开放式API插件系统,允许第三方开发者或服务商轻松将其服务集成到AntSK,不断增强应用功能。
|
||||
|
||||
- **联网搜索**:AntSK,实时获取最新信息,确保用户接受到的资料总是最及时、最相关的。
|
||||
|
||||
- **GPTs 生成**:此平台支持创建个性化的GPT模型,尝试构建您自己的GPT模型。
|
||||
|
||||
- **API接口发布**:将内部功能以API的形式对外提供,便于开发者将AntSK 集成进其他应用,增强应用智慧。
|
||||
|
||||
- **模型管理**:适配和管理集成不同厂商的不同模型。
|
||||
- **API插件系统**:开放式API插件系统,允许第三方开发者或服务商轻松将其服务集成到AntSK,不断增强应用功能。
|
||||
|
||||
- **.Net插件系统(规划中)**:开放式dll插件系统,允许第三方开发者或服务商轻松将其业务功能通过标准格式的代码生成dll后集成到AntSK,不断增强应用功能。
|
||||
|
||||
- **联网搜索**:AntSK,实时获取最新信息,确保用户接受到的资料总是最及时、最相关的。
|
||||
|
||||
- **模型管理**:适配和管理集成不同厂商的不同模型。并且支持llama.cpp所支持的gguf类型的模型离线运行
|
||||
|
||||
## 应用场景
|
||||
|
||||
@@ -63,9 +65,9 @@ AntSK 适用于多种业务场景,例如:
|
||||
|
||||
在这里我使用的是Postgres 作为数据存储和向量存储,因为Semantic Kernel和Kernel Memory都支持他,当然你也可以换成其他的。
|
||||
|
||||
模型默认支持openai,如果需要使用azure openai需要调整SK的依赖注入,也可以使用one-api进行集成。
|
||||
模型默认支持openai、azure openai 和llama支持的gguf本地模型,如果需要使用其他模型,可以使用one-api进行集成。
|
||||
|
||||
Login是默认的登陆账号和密码
|
||||
配置文件中的Login配置是默认的登陆账号和密码
|
||||
|
||||
需要配置如下的配置文件
|
||||
|
||||
@@ -144,12 +146,10 @@ model/xxx.gguf
|
||||
DBConnection.DbType
|
||||
//连接字符串,需要根据不同DB类型,用对应的字符串
|
||||
DBConnection.ConnectionStrings
|
||||
//可以使用符合openai格式的在线API(国产模型使用one-api转接) ,也可以使用AntSK自带的llama api,ip和端口是AntSK启动地址
|
||||
OpenAIOption.EndPoint
|
||||
//模型秘钥,如果使用本地模型可以默认NotNull 这里不能用中文
|
||||
OpenAIOption.Key
|
||||
|
||||
//向量存储的类型,支持 Postgres Disk Memory ,其中Postgres需要配置 ConnectionString
|
||||
KernelMemory.VectorDb
|
||||
|
||||
//本地模型使用的运行方式 GUP CPU ,如果用在线API 这个随意使用一个即可
|
||||
LLamaSharp.RunType
|
||||
//本地会话模型的模型路径 注意区分linux和windows盘符不同
|
||||
@@ -157,6 +157,7 @@ LLamaSharp.Chat
|
||||
//本地向量模型的模型路径 注意区分linux和windows盘符不同
|
||||
LLamaSharp.Embedding
|
||||
//默认管理员账号密码
|
||||
|
||||
Login
|
||||
//导入异步处理的线程数,使用在线API可以高一点,本地模型建议1 否则容易内存溢出崩掉
|
||||
BackgroundTaskBroker.ImportKMSTask.WorkerCount
|
||||
|
||||
@@ -3,7 +3,7 @@ version: '3.8'
|
||||
services:
|
||||
antsk:
|
||||
container_name: antsk
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.6.2
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.8
|
||||
ports:
|
||||
- 5000:5000
|
||||
networks:
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
antsk:
|
||||
container_name: antsk
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.6.2
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.8
|
||||
ports:
|
||||
- 5000:5000
|
||||
networks:
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AntDesign.Charts" Version="0.5.1" />
|
||||
<PackageReference Include="AntDesign.ProLayout" Version="0.17.3" />
|
||||
<PackageReference Include="AntDesign.ProLayout" Version="0.18.0" />
|
||||
|
||||
<PackageReference Include="AutoMapper" Version="8.1.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="MarkdownSharp" Version="2.0.5" />
|
||||
<PackageReference Include="Markdig" Version="0.35.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.145" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||
<PackageReference Include="RestSharp" Version="110.2.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.4.0-alpha" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.30.240227.1" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.30.240227.1" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.32.240308.1" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.32.240308.1" />
|
||||
|
||||
<PackageReference Include="LLamaSharp" Version="0.10.0" />
|
||||
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.10.0" />
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AntSk.LLM\AntSK.LLM.csproj" />
|
||||
<ProjectReference Include="..\MiddleWare\AntSK.BackgroundTask\AntSK.BackgroundTask.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@
|
||||
<param name="history"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Domain.Service.FunctionService.SearchMarkedMethods">
|
||||
<summary>
|
||||
查询程序集中的方法委托,后续利用Source Generators生成
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Domain.Service.KernelService.GetKernelByApp(AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
获取kernel实例,依赖注入不好按每个用户去Import不同的插件,所以每次new一个新的kernel
|
||||
@@ -252,6 +257,11 @@
|
||||
插件列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.NativeFunctionList">
|
||||
<summary>
|
||||
本地函数列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.KmsIdList">
|
||||
<summary>
|
||||
知识库ID列表
|
||||
@@ -740,5 +750,130 @@
|
||||
</summary>
|
||||
<returns>The full path to samples/plugins</returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Domain.Utils.XmlCommentHelper">
|
||||
<summary>
|
||||
注释辅助类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.LoadAll">
|
||||
<summary>
|
||||
从当前dll文件中加载所有的xml文件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.LoadXml(System.String[])">
|
||||
<summary>
|
||||
从xml中加载
|
||||
</summary>
|
||||
<param name="xmls"></param>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.Load(System.String[])">
|
||||
<summary>
|
||||
从文件中加载
|
||||
</summary>
|
||||
<param name="xmlFiles"></param>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.Load(System.IO.Stream[])">
|
||||
<summary>
|
||||
从流中加载
|
||||
</summary>
|
||||
<param name="streams"></param>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetTypeComment(System.Type,System.String,System.Boolean)">
|
||||
<summary>
|
||||
读取类型中的注释
|
||||
</summary>
|
||||
<param name="type">类型</param>
|
||||
<param name="xPath">注释路径</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetFieldOrPropertyComment(System.Reflection.MemberInfo,System.String,System.Boolean)">
|
||||
<summary>
|
||||
读取字段或者属性的注释
|
||||
</summary>
|
||||
<param name="fieldOrPropertyInfo">字段或者属性</param>
|
||||
<param name="xPath">注释路径</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetMethodComment(System.Reflection.MethodInfo,System.String,System.Boolean)">
|
||||
<summary>
|
||||
读取方法中的注释
|
||||
</summary>
|
||||
<param name="methodInfo">方法</param>
|
||||
<param name="xPath">注释路径</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetMethodReturnComment(System.Reflection.MethodInfo,System.Boolean)">
|
||||
<summary>
|
||||
读取方法中的返回值注释
|
||||
</summary>
|
||||
<param name="methodInfo">方法</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetParameterComment(System.Reflection.ParameterInfo,System.Boolean)">
|
||||
<summary>
|
||||
读取参数的注释
|
||||
</summary>
|
||||
<param name="parameterInfo">参数</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetParameterComments(System.Reflection.MethodInfo,System.Boolean)">
|
||||
<summary>
|
||||
读取方法的所有参数的注释
|
||||
</summary>
|
||||
<param name="methodInfo">方法</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetComment(System.String,System.String,System.Boolean)">
|
||||
<summary>
|
||||
读取指定名称节点的注释
|
||||
</summary>
|
||||
<param name="name">节点名称</param>
|
||||
<param name="xPath">注释路径</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetSummary(System.String,System.Boolean)">
|
||||
<summary>
|
||||
读取指定节点的summary注释
|
||||
</summary>
|
||||
<param name="name">节点名称</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetExample(System.String,System.Boolean)">
|
||||
<summary>
|
||||
读取指定节点的example注释
|
||||
</summary>
|
||||
<param name="name">节点名称</param>
|
||||
<param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetMemberNameForMethod(System.Reflection.MethodInfo)">
|
||||
<summary>
|
||||
获取方法的节点名称
|
||||
</summary>
|
||||
<param name="method"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetMemberNameForType(System.Type)">
|
||||
<summary>
|
||||
获取类型的节点名称
|
||||
</summary>
|
||||
<param name="type"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.XmlCommentHelper.GetMemberNameForFieldOrProperty(System.Reflection.MemberInfo)">
|
||||
<summary>
|
||||
获取字段或者属性的节点名称
|
||||
</summary>
|
||||
<param name="fieldOrPropertyInfo"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
8
src/AntSK.Domain/Common/AntSkFunctionAttribute.cs
Normal file
8
src/AntSK.Domain/Common/AntSkFunctionAttribute.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace AntSK.Domain.Common
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class AntSkFunctionAttribute : Attribute
|
||||
{
|
||||
// 自定义的ActionAttribute
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ namespace AntSK.Domain.Domain.Other
|
||||
{
|
||||
ContextSize = lsConfig?.ContextSize ?? 2048,
|
||||
Seed = lsConfig?.Seed ?? 0,
|
||||
GpuLayerCount = lsConfig?.GpuLayerCount ?? 20,
|
||||
GpuLayerCount = lsConfig?.GpuLayerCount ?? 10,
|
||||
EmbeddingMode = true
|
||||
};
|
||||
var weights = LLamaWeights.LoadFromFile(parameters);
|
||||
|
||||
@@ -11,15 +11,15 @@ using System.Threading.Tasks;
|
||||
using AntSK.Domain.Utils;
|
||||
using Microsoft.KernelMemory;
|
||||
using AntSK.Domain.Model;
|
||||
using MarkdownSharp;
|
||||
using AntSK.Domain.Domain.Dto;
|
||||
using Markdig;
|
||||
|
||||
namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
[ServiceDescription(typeof(IChatService), ServiceLifetime.Scoped)]
|
||||
public class ChatService(
|
||||
IKernelService _kernelService,
|
||||
IKMService _kMService ,
|
||||
IKMService _kMService,
|
||||
IKmsDetails_Repositories _kmsDetails_Repositories
|
||||
) : IChatService
|
||||
{
|
||||
@@ -40,12 +40,11 @@ namespace AntSK.Domain.Domain.Service
|
||||
var _kernel = _kernelService.GetKernelByApp(app);
|
||||
var temperature = app.Temperature / 100;//存的是0~100需要缩小
|
||||
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
|
||||
if (!string.IsNullOrEmpty(app.ApiFunctionList))
|
||||
if (!string.IsNullOrEmpty(app.ApiFunctionList)|| !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
|
||||
{
|
||||
_kernelService.ImportFunctionsByApp(app, _kernel);
|
||||
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
|
||||
}
|
||||
|
||||
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
|
||||
var chatResult = _kernel.InvokeStreamingAsync(function: func, arguments: new KernelArguments() { ["input"] = $"{history}{Environment.NewLine} user:{questions}" });
|
||||
await foreach (var content in chatResult)
|
||||
@@ -54,7 +53,7 @@ namespace AntSK.Domain.Domain.Service
|
||||
}
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, List<RelevantSource> relevantSources=null)
|
||||
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, List<RelevantSource> relevantSources = null)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernelByApp(app);
|
||||
//知识库问答
|
||||
@@ -78,14 +77,13 @@ namespace AntSK.Domain.Domain.Service
|
||||
|
||||
if (relevantSources.IsNotNull())
|
||||
{
|
||||
var markdown = new Markdown();
|
||||
string sourceName = item.SourceName;
|
||||
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == item.SourceName);
|
||||
if (fileDetail.IsNotNull())
|
||||
{
|
||||
sourceName = fileDetail.FileName;
|
||||
}
|
||||
relevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = markdown.Transform(part.Text), Relevance = part.Relevance });
|
||||
relevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = Markdown.ToHtml(part.Text), Relevance = part.Relevance });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,12 +92,11 @@ namespace AntSK.Domain.Domain.Service
|
||||
arguments: new KernelArguments() { ["doc"] = dataMsg, ["history"] = history, ["questions"] = questions });
|
||||
|
||||
MessageInfo info = null;
|
||||
var markdown1 = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
yield return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/AntSK.Domain/Domain/Service/FunctionService.cs
Normal file
69
src/AntSK.Domain/Domain/Service/FunctionService.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using AntSK.Domain.Common;
|
||||
using AntSK.Domain.Utils;
|
||||
|
||||
namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
public class FunctionService
|
||||
{
|
||||
private readonly Dictionary<string, Func<object[], object>> _methodCache;
|
||||
private readonly Dictionary<string, (string Description, (Type ParameterType, string Description) ReturnType, (string ParameterName, Type ParameterType, string Description)[] Parameters)> _methodInfos;
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly Assembly[] _assemblies;
|
||||
|
||||
public FunctionService(IServiceProvider serviceProvider, Assembly[] assemblies)
|
||||
{
|
||||
_methodCache = [];
|
||||
_methodInfos = [];
|
||||
_serviceProvider = serviceProvider;
|
||||
_assemblies = assemblies;
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<object[], object>> Functions => _methodCache;
|
||||
public Dictionary<string, (string Description, (Type ParameterType, string Description) ReturnType, (string ParameterName, Type ParameterType, string Description)[] Parameters)> MethodInfos => _methodInfos;
|
||||
|
||||
/// <summary>
|
||||
/// 查询程序集中的方法委托,后续利用Source Generators生成
|
||||
/// </summary>
|
||||
public void SearchMarkedMethods()
|
||||
{
|
||||
var markedMethods = new List<MethodInfo>();
|
||||
|
||||
_methodCache.Clear();
|
||||
_methodInfos.Clear();
|
||||
|
||||
foreach (var assembly in _assemblies)
|
||||
{
|
||||
// 从缓存中获取标记了ActionAttribute的方法
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
markedMethods.AddRange(type.GetMethods().Where(m => m.GetCustomAttributes(typeof(AntSkFunctionAttribute), true).Length > 0));
|
||||
}
|
||||
}
|
||||
|
||||
// 构建方法调用
|
||||
foreach (var method in markedMethods)
|
||||
{
|
||||
var key = $"{method.DeclaringType.Assembly.GetName().Name}_{method.DeclaringType.Name}_{method.Name}";
|
||||
_methodCache.TryAdd(key, arguments =>
|
||||
{
|
||||
var instance = _serviceProvider.GetService(method.DeclaringType);
|
||||
return method.Invoke(instance, arguments);
|
||||
});
|
||||
|
||||
var xmlCommentHelper = new XmlCommentHelper();
|
||||
xmlCommentHelper.LoadAll();
|
||||
|
||||
var description = xmlCommentHelper.GetMethodComment(method);
|
||||
var dict = xmlCommentHelper.GetParameterComments(method);
|
||||
|
||||
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType, dict[x.Name])).ToArray();
|
||||
var returnType = xmlCommentHelper.GetMethodReturnComment(method);
|
||||
|
||||
_methodInfos.TryAdd(key, (description, (method.ReflectedType, returnType), parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,46 +22,54 @@ namespace AntSK.Domain.Domain.Service
|
||||
IAIModels_Repositories _aIModels_Repositories
|
||||
) : IKMService
|
||||
{
|
||||
private MemoryServerless _memory;
|
||||
|
||||
public MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null)
|
||||
{
|
||||
//获取KMS配置
|
||||
var kms = _kmss_Repositories.GetFirst(p => p.Id == kmsID);
|
||||
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.ChatModelID);
|
||||
var embedModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.EmbeddingModelID);
|
||||
|
||||
//http代理
|
||||
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
|
||||
var embeddingHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(embedModel.EndPoint);
|
||||
|
||||
//搜索配置
|
||||
if (searchClientConfig.IsNull())
|
||||
//if (_memory.IsNull())
|
||||
{
|
||||
searchClientConfig = new SearchClientConfig
|
||||
//获取KMS配置
|
||||
var kms = _kmss_Repositories.GetFirst(p => p.Id == kmsID);
|
||||
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.ChatModelID);
|
||||
var embedModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.EmbeddingModelID);
|
||||
|
||||
//http代理
|
||||
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
|
||||
var embeddingHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(embedModel.EndPoint);
|
||||
|
||||
//搜索配置
|
||||
if (searchClientConfig.IsNull())
|
||||
{
|
||||
MaxAskPromptSize = 2048,
|
||||
MaxMatchesCount = 3,
|
||||
AnswerTokens = 1000,
|
||||
EmptyAnswer = "知识库未搜索到相关内容"
|
||||
};
|
||||
searchClientConfig = new SearchClientConfig
|
||||
{
|
||||
MaxAskPromptSize = 2048,
|
||||
MaxMatchesCount = 3,
|
||||
AnswerTokens = 1000,
|
||||
EmptyAnswer = "知识库未搜索到相关内容"
|
||||
};
|
||||
}
|
||||
|
||||
var memoryBuild = new KernelMemoryBuilder()
|
||||
.WithSearchClientConfig(searchClientConfig)
|
||||
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
|
||||
{
|
||||
MaxTokensPerLine = kms.MaxTokensPerLine,
|
||||
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
|
||||
OverlappingTokens = kms.OverlappingTokens
|
||||
});
|
||||
//加载huihu 模型
|
||||
WithTextGenerationByAIType(memoryBuild, chatModel, chatHttpClient);
|
||||
//加载向量模型
|
||||
WithTextEmbeddingGenerationByAIType(memoryBuild, embedModel, embeddingHttpClient);
|
||||
//加载向量库
|
||||
WithMemoryDbByVectorDB(memoryBuild, _config);
|
||||
|
||||
_memory = memoryBuild.Build<MemoryServerless>();
|
||||
return _memory;
|
||||
}
|
||||
|
||||
var memory = new KernelMemoryBuilder()
|
||||
.WithSearchClientConfig(searchClientConfig)
|
||||
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
|
||||
{
|
||||
MaxTokensPerLine = kms.MaxTokensPerLine,
|
||||
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
|
||||
OverlappingTokens = kms.OverlappingTokens
|
||||
});
|
||||
//加载huihu 模型
|
||||
WithTextGenerationByAIType(memory, chatModel, chatHttpClient);
|
||||
//加载向量模型
|
||||
WithTextEmbeddingGenerationByAIType(memory, embedModel, embeddingHttpClient);
|
||||
//加载向量库
|
||||
WithMemoryDbByVectorDB(memory, _config);
|
||||
|
||||
var result = memory.Build<MemoryServerless>();
|
||||
return result;
|
||||
//else {
|
||||
// return _memory;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.LLM.SparkDesk;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Domain.Other;
|
||||
using AntSK.Domain.Model;
|
||||
@@ -11,7 +12,9 @@ using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Plugins.Core;
|
||||
using Microsoft.SemanticKernel.TextGeneration;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
using AntSK.LLM.Mock;
|
||||
|
||||
namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
@@ -20,15 +23,21 @@ namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
private readonly IApis_Repositories _apis_Repositories;
|
||||
private readonly IAIModels_Repositories _aIModels_Repositories;
|
||||
private readonly FunctionService _functionService;
|
||||
|
||||
public KernelService(
|
||||
IApis_Repositories apis_Repositories,
|
||||
IAIModels_Repositories aIModels_Repositories
|
||||
)
|
||||
IAIModels_Repositories aIModels_Repositories,
|
||||
FunctionService functionService)
|
||||
{
|
||||
_apis_Repositories = apis_Repositories;
|
||||
_aIModels_Repositories = aIModels_Repositories;
|
||||
|
||||
_functionService = functionService;
|
||||
}
|
||||
|
||||
private Kernel _kernel;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取kernel实例,依赖注入不好按每个用户去Import不同的插件,所以每次new一个新的kernel
|
||||
/// </summary>
|
||||
@@ -37,20 +46,26 @@ namespace AntSK.Domain.Domain.Service
|
||||
/// <returns></returns>
|
||||
public Kernel GetKernelByApp(Apps app)
|
||||
{
|
||||
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);
|
||||
//if (_kernel.IsNull())
|
||||
{
|
||||
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);
|
||||
|
||||
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
|
||||
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
|
||||
|
||||
var builder = Kernel.CreateBuilder();
|
||||
WithTextGenerationByAIType(builder, chatModel, chatHttpClient);
|
||||
var builder = Kernel.CreateBuilder();
|
||||
WithTextGenerationByAIType(builder, app, chatModel, chatHttpClient);
|
||||
|
||||
|
||||
var kernel = builder.Build();
|
||||
RegisterPluginsWithKernel(kernel);
|
||||
return kernel;
|
||||
_kernel = builder.Build();
|
||||
RegisterPluginsWithKernel(_kernel);
|
||||
return _kernel;
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// return _kernel;
|
||||
//}
|
||||
}
|
||||
|
||||
private void WithTextGenerationByAIType(IKernelBuilder builder, AIModels chatModel, HttpClient chatHttpClient)
|
||||
private void WithTextGenerationByAIType(IKernelBuilder builder, Apps app, AIModels chatModel, HttpClient chatHttpClient)
|
||||
{
|
||||
switch (chatModel.AIType)
|
||||
{
|
||||
@@ -60,6 +75,7 @@ namespace AntSK.Domain.Domain.Service
|
||||
apiKey: chatModel.ModelKey,
|
||||
httpClient: chatHttpClient);
|
||||
break;
|
||||
|
||||
case Model.Enum.AIType.AzureOpenAI:
|
||||
builder.AddAzureOpenAIChatCompletion(
|
||||
deploymentName: chatModel.ModelName,
|
||||
@@ -67,11 +83,20 @@ namespace AntSK.Domain.Domain.Service
|
||||
endpoint: chatModel.EndPoint
|
||||
);
|
||||
break;
|
||||
|
||||
case Model.Enum.AIType.LLamaSharp:
|
||||
var (weights, parameters) = LLamaConfig.GetLLamaConfig(chatModel.ModelName);
|
||||
var ex = new StatelessExecutor(weights, parameters);
|
||||
builder.Services.AddKeyedSingleton<ITextGenerationService>("local-llama", new LLamaSharpTextCompletion(ex));
|
||||
break;
|
||||
|
||||
case Model.Enum.AIType.SparkDesk:
|
||||
var options = new SparkDeskOptions { AppId = chatModel.EndPoint, ApiSecret = chatModel.ModelKey, ApiKey = chatModel.ModelName, ModelVersion = Sdcb.SparkDesk.ModelVersion.V3_5 };
|
||||
builder.Services.AddKeyedSingleton<ITextGenerationService>("spark-desk", new SparkDeskTextCompletion(options, app.Id));
|
||||
break;
|
||||
case Model.Enum.AIType.Mock:
|
||||
builder.Services.AddKeyedSingleton<ITextGenerationService>("mock", new MockTextCompletion());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,18 +107,26 @@ namespace AntSK.Domain.Domain.Service
|
||||
/// <param name="_kernel"></param>
|
||||
public void ImportFunctionsByApp(Apps app, Kernel _kernel)
|
||||
{
|
||||
//开启自动插件调用
|
||||
var apiIdList = app.ApiFunctionList.Split(",");
|
||||
var apiList = _apis_Repositories.GetList(p => apiIdList.Contains(p.Id));
|
||||
List<KernelFunction> functions = new List<KernelFunction>();
|
||||
var plugin = _kernel.Plugins.FirstOrDefault(p => p.Name == "ApiFunctions");
|
||||
//插件不能重复注册,否则会异常
|
||||
if (_kernel.Plugins.Any(p => p.Name == "AntSkFunctions"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<KernelFunction> apiFunctions = new List<KernelFunction>();
|
||||
|
||||
//API插件
|
||||
if (!string.IsNullOrWhiteSpace(app.ApiFunctionList))
|
||||
{
|
||||
//开启自动插件调用
|
||||
var apiIdList = app.ApiFunctionList.Split(",");
|
||||
var apiList = _apis_Repositories.GetList(p => apiIdList.Contains(p.Id));
|
||||
|
||||
foreach (var api in apiList)
|
||||
{
|
||||
switch (api.Method)
|
||||
{
|
||||
case HttpMethodType.Get:
|
||||
functions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
|
||||
apiFunctions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -126,8 +159,9 @@ namespace AntSK.Domain.Domain.Service
|
||||
}
|
||||
}, api.Name, $"{api.Describe}"));
|
||||
break;
|
||||
|
||||
case HttpMethodType.Post:
|
||||
functions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
|
||||
apiFunctions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -155,16 +189,33 @@ namespace AntSK.Domain.Domain.Service
|
||||
break;
|
||||
}
|
||||
}
|
||||
_kernel.ImportPluginFromFunctions("ApiFunctions", functions);
|
||||
}
|
||||
|
||||
//本地函数插件
|
||||
if (!string.IsNullOrWhiteSpace(app.NativeFunctionList))//需要添加判断应用是否开启了本地函数插件
|
||||
{
|
||||
var nativeIdList = app.NativeFunctionList.Split(",");
|
||||
|
||||
_functionService.SearchMarkedMethods();
|
||||
foreach (var func in _functionService.Functions)
|
||||
{
|
||||
if (nativeIdList.Contains(func.Key))
|
||||
{
|
||||
var methodInfo = _functionService.MethodInfos[func.Key];
|
||||
var parameters = methodInfo.Parameters.Select(x => new KernelParameterMetadata(x.ParameterName) { ParameterType = x.ParameterType, Description = x.Description });
|
||||
var returnType = new KernelReturnParameterMetadata() { ParameterType = methodInfo.ReturnType.ParameterType, Description = methodInfo.ReturnType.Description };
|
||||
apiFunctions.Add(_kernel.CreateFunctionFromMethod((object[] args) => func.Value(args), func.Key, methodInfo.Description, parameters, returnType));
|
||||
}
|
||||
}
|
||||
}
|
||||
_kernel.ImportPluginFromFunctions("AntSkFunctions", apiFunctions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册默认插件
|
||||
/// </summary>
|
||||
/// <param name="kernel"></param>
|
||||
void RegisterPluginsWithKernel(Kernel kernel)
|
||||
private void RegisterPluginsWithKernel(Kernel kernel)
|
||||
{
|
||||
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
|
||||
kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
|
||||
@@ -187,4 +238,4 @@ namespace AntSK.Domain.Domain.Service
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@
|
||||
{
|
||||
OpenAI = 1,
|
||||
AzureOpenAI = 2,
|
||||
LLamaSharp = 3
|
||||
LLamaSharp=3,
|
||||
SparkDesk=4,
|
||||
Mock=5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
14
src/AntSK.Domain/Model/Enum/AppType.cs
Normal file
14
src/AntSK.Domain/Model/Enum/AppType.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Model.Enum
|
||||
{
|
||||
public enum AppType
|
||||
{
|
||||
chat=1,
|
||||
kms=2
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,5 @@
|
||||
{
|
||||
Get = 1,
|
||||
Post = 2,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/AntSK.Domain/Model/hfmirror/HfModel.cs
Normal file
40
src/AntSK.Domain/Model/hfmirror/HfModel.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Model.hfmirror
|
||||
{
|
||||
public class HfModel
|
||||
{
|
||||
public List<HfModels> models { get; set; }
|
||||
public int numItemsPerPage { get; set; }
|
||||
public int numTotalItems { get; set; }
|
||||
public int pageIndex { get; set; }
|
||||
}
|
||||
public class HfModels
|
||||
{
|
||||
public string Author { get; set; }
|
||||
public HfAuthorData AuthorData { get; set; }
|
||||
public int Downloads { get; set; }
|
||||
public bool Gated { get; set; }
|
||||
public string Id { get; set; }
|
||||
public DateTime LastModified { get; set; }
|
||||
public int Likes { get; set; }
|
||||
public string PipelineTag { get; set; }
|
||||
public bool Private { get; set; }
|
||||
public string RepoType { get; set; }
|
||||
public bool IsLikedByUser { get; set; }
|
||||
}
|
||||
|
||||
public class HfAuthorData
|
||||
{
|
||||
public string AvatarUrl { get; set; }
|
||||
public string Fullname { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public bool IsHf { get; set; }
|
||||
public bool IsEnterprise { get; set; }
|
||||
}
|
||||
}
|
||||
17
src/AntSK.Domain/Model/hfmirror/HfModelDetail.cs
Normal file
17
src/AntSK.Domain/Model/hfmirror/HfModelDetail.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Model.hfmirror
|
||||
{
|
||||
public class HfModelDetail
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Size { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
public string Time { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,7 @@
|
||||
public static string Chat { get; set; }
|
||||
|
||||
public static string Embedding { get; set; }
|
||||
|
||||
public static string FileDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,15 @@ namespace AntSK.Domain.Repositories
|
||||
/// <summary>
|
||||
/// 插件列表
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "varchar(1000)")]
|
||||
public string? ApiFunctionList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 本地函数列表
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "varchar(1000)")]
|
||||
public string? NativeFunctionList { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 知识库ID列表
|
||||
|
||||
375
src/AntSK.Domain/Utils/XmlCommentHelper.cs
Normal file
375
src/AntSK.Domain/Utils/XmlCommentHelper.cs
Normal file
@@ -0,0 +1,375 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.XPath;
|
||||
|
||||
namespace AntSK.Domain.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 注释辅助类
|
||||
/// </summary>
|
||||
public class XmlCommentHelper
|
||||
{
|
||||
private static Regex RefTagPattern = new Regex(@"<(see|paramref) (name|cref)=""([TPF]{1}:)?(?<display>.+?)"" ?/>");
|
||||
private static Regex CodeTagPattern = new Regex(@"<c>(?<display>.+?)</c>");
|
||||
private static Regex ParaTagPattern = new Regex(@"<para>(?<display>.+?)</para>", RegexOptions.Singleline);
|
||||
|
||||
List<XPathNavigator> navigators = new List<XPathNavigator>();
|
||||
|
||||
/// <summary>
|
||||
/// 从当前dll文件中加载所有的xml文件
|
||||
/// </summary>
|
||||
public void LoadAll()
|
||||
{
|
||||
var files = Directory.GetFiles(Directory.GetCurrentDirectory());
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (string.Equals(Path.GetExtension(file), ".xml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Load(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 从xml中加载
|
||||
/// </summary>
|
||||
/// <param name="xmls"></param>
|
||||
public void LoadXml(params string[] xmls)
|
||||
{
|
||||
foreach (var xml in xmls)
|
||||
{
|
||||
Load(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 从文件中加载
|
||||
/// </summary>
|
||||
/// <param name="xmlFiles"></param>
|
||||
public void Load(params string[] xmlFiles)
|
||||
{
|
||||
foreach (var xmlFile in xmlFiles)
|
||||
{
|
||||
var doc = new XPathDocument(xmlFile);
|
||||
navigators.Add(doc.CreateNavigator());
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 从流中加载
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
public void Load(params Stream[] streams)
|
||||
{
|
||||
foreach (var stream in streams)
|
||||
{
|
||||
var doc = new XPathDocument(stream);
|
||||
navigators.Add(doc.CreateNavigator());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取类型中的注释
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="xPath">注释路径</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetTypeComment(Type type, string xPath = "summary", bool humanize = true)
|
||||
{
|
||||
var typeMemberName = GetMemberNameForType(type);
|
||||
return GetComment(typeMemberName, xPath, humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取字段或者属性的注释
|
||||
/// </summary>
|
||||
/// <param name="fieldOrPropertyInfo">字段或者属性</param>
|
||||
/// <param name="xPath">注释路径</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetFieldOrPropertyComment(MemberInfo fieldOrPropertyInfo, string xPath = "summary", bool humanize = true)
|
||||
{
|
||||
var fieldOrPropertyMemberName = GetMemberNameForFieldOrProperty(fieldOrPropertyInfo);
|
||||
return GetComment(fieldOrPropertyMemberName, xPath, humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取方法中的注释
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">方法</param>
|
||||
/// <param name="xPath">注释路径</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetMethodComment(MethodInfo methodInfo, string xPath = "summary", bool humanize = true)
|
||||
{
|
||||
var methodMemberName = GetMemberNameForMethod(methodInfo);
|
||||
return GetComment(methodMemberName, xPath, humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取方法中的返回值注释
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">方法</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetMethodReturnComment(MethodInfo methodInfo, bool humanize = true)
|
||||
{
|
||||
return GetMethodComment(methodInfo, "returns", humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取参数的注释
|
||||
/// </summary>
|
||||
/// <param name="parameterInfo">参数</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetParameterComment(ParameterInfo parameterInfo, bool humanize = true)
|
||||
{
|
||||
if (!(parameterInfo.Member is MethodInfo methodInfo)) return string.Empty;
|
||||
|
||||
var methodMemberName = GetMemberNameForMethod(methodInfo);
|
||||
return GetComment(methodMemberName, $"param[@name='{parameterInfo.Name}']", humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取方法的所有参数的注释
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">方法</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, string> GetParameterComments(MethodInfo methodInfo, bool humanize = true)
|
||||
{
|
||||
var parameterInfos = methodInfo.GetParameters();
|
||||
Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||
foreach (var parameterInfo in parameterInfos)
|
||||
{
|
||||
dict[parameterInfo.Name] = GetParameterComment(parameterInfo, humanize);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取指定名称节点的注释
|
||||
/// </summary>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="xPath">注释路径</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetComment(string name, string xPath, bool humanize = true)
|
||||
{
|
||||
foreach (var _xmlNavigator in navigators)
|
||||
{
|
||||
var typeSummaryNode = _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{name}']/{xPath.Trim('/', '\\')}");
|
||||
|
||||
if (typeSummaryNode != null)
|
||||
{
|
||||
return humanize ? Humanize(typeSummaryNode.InnerXml) : typeSummaryNode.InnerXml;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取指定节点的summary注释
|
||||
/// </summary>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetSummary(string name, bool humanize = true)
|
||||
{
|
||||
return GetComment(name, "summary", humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取指定节点的example注释
|
||||
/// </summary>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="humanize">可读性优化(比如:去掉xml标记)</param>
|
||||
/// <returns></returns>
|
||||
public string GetExample(string name, bool humanize = true)
|
||||
{
|
||||
return GetComment(name, "example", humanize);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取方法的节点名称
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <returns></returns>
|
||||
public string GetMemberNameForMethod(MethodInfo method)
|
||||
{
|
||||
var builder = new StringBuilder("M:");
|
||||
|
||||
builder.Append(QualifiedNameFor(method.DeclaringType));
|
||||
builder.Append($".{method.Name}");
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Any())
|
||||
{
|
||||
var parametersNames = parameters.Select(p =>
|
||||
{
|
||||
return p.ParameterType.IsGenericParameter
|
||||
? $"`{p.ParameterType.GenericParameterPosition}"
|
||||
: QualifiedNameFor(p.ParameterType, expandGenericArgs: true);
|
||||
});
|
||||
builder.Append($"({string.Join(",", parametersNames)})");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取类型的节点名称
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public string GetMemberNameForType(Type type)
|
||||
{
|
||||
var builder = new StringBuilder("T:");
|
||||
builder.Append(QualifiedNameFor(type));
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取字段或者属性的节点名称
|
||||
/// </summary>
|
||||
/// <param name="fieldOrPropertyInfo"></param>
|
||||
/// <returns></returns>
|
||||
public string GetMemberNameForFieldOrProperty(MemberInfo fieldOrPropertyInfo)
|
||||
{
|
||||
var builder = new StringBuilder((fieldOrPropertyInfo.MemberType & MemberTypes.Field) != 0 ? "F:" : "P:");
|
||||
builder.Append(QualifiedNameFor(fieldOrPropertyInfo.DeclaringType));
|
||||
builder.Append($".{fieldOrPropertyInfo.Name}");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private string QualifiedNameFor(Type type, bool expandGenericArgs = false)
|
||||
{
|
||||
if (type.IsArray)
|
||||
return $"{QualifiedNameFor(type.GetElementType(), expandGenericArgs)}[]";
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (!string.IsNullOrEmpty(type.Namespace))
|
||||
builder.Append($"{type.Namespace}.");
|
||||
|
||||
if (type.IsNested)
|
||||
{
|
||||
builder.Append($"{string.Join(".", GetNestedTypeNames(type))}.");
|
||||
}
|
||||
|
||||
if (type.IsConstructedGenericType && expandGenericArgs)
|
||||
{
|
||||
var nameSansGenericArgs = type.Name.Split('`').First();
|
||||
builder.Append(nameSansGenericArgs);
|
||||
|
||||
var genericArgsNames = type.GetGenericArguments().Select(t =>
|
||||
{
|
||||
return t.IsGenericParameter
|
||||
? $"`{t.GenericParameterPosition}"
|
||||
: QualifiedNameFor(t, true);
|
||||
});
|
||||
|
||||
builder.Append($"{{{string.Join(",", genericArgsNames)}}}");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(type.Name);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
private IEnumerable<string> GetNestedTypeNames(Type type)
|
||||
{
|
||||
if (!type.IsNested || type.DeclaringType == null) yield break;
|
||||
|
||||
foreach (var nestedTypeName in GetNestedTypeNames(type.DeclaringType))
|
||||
{
|
||||
yield return nestedTypeName;
|
||||
}
|
||||
|
||||
yield return type.DeclaringType.Name;
|
||||
}
|
||||
private string Humanize(string text)
|
||||
{
|
||||
if (text == null)
|
||||
throw new ArgumentNullException("text");
|
||||
|
||||
//Call DecodeXml at last to avoid entities like < and > to break valid xml
|
||||
text = NormalizeIndentation(text);
|
||||
text = HumanizeRefTags(text);
|
||||
text = HumanizeCodeTags(text);
|
||||
text = HumanizeParaTags(text);
|
||||
text = DecodeXml(text);
|
||||
return text;
|
||||
}
|
||||
private string NormalizeIndentation(string text)
|
||||
{
|
||||
string[] lines = text.Split('\n');
|
||||
string padding = GetCommonLeadingWhitespace(lines);
|
||||
|
||||
int padLen = padding == null ? 0 : padding.Length;
|
||||
|
||||
// remove leading padding from each line
|
||||
for (int i = 0, l = lines.Length; i < l; ++i)
|
||||
{
|
||||
string line = lines[i].TrimEnd('\r'); // remove trailing '\r'
|
||||
|
||||
if (padLen != 0 && line.Length >= padLen && line.Substring(0, padLen) == padding)
|
||||
line = line.Substring(padLen);
|
||||
|
||||
lines[i] = line;
|
||||
}
|
||||
|
||||
// remove leading empty lines, but not all leading padding
|
||||
// remove all trailing whitespace, regardless
|
||||
return string.Join("\r\n", lines.SkipWhile(x => string.IsNullOrWhiteSpace(x))).TrimEnd();
|
||||
}
|
||||
private string GetCommonLeadingWhitespace(string[] lines)
|
||||
{
|
||||
if (null == lines)
|
||||
throw new ArgumentException("lines");
|
||||
|
||||
if (lines.Length == 0)
|
||||
return null;
|
||||
|
||||
string[] nonEmptyLines = lines
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.ToArray();
|
||||
|
||||
if (nonEmptyLines.Length < 1)
|
||||
return null;
|
||||
|
||||
int padLen = 0;
|
||||
|
||||
// use the first line as a seed, and see what is shared over all nonEmptyLines
|
||||
string seed = nonEmptyLines[0];
|
||||
for (int i = 0, l = seed.Length; i < l; ++i)
|
||||
{
|
||||
if (!char.IsWhiteSpace(seed, i))
|
||||
break;
|
||||
|
||||
if (nonEmptyLines.Any(line => line[i] != seed[i]))
|
||||
break;
|
||||
|
||||
++padLen;
|
||||
}
|
||||
|
||||
if (padLen > 0)
|
||||
return seed.Substring(0, padLen);
|
||||
|
||||
return null;
|
||||
}
|
||||
private string HumanizeRefTags(string text)
|
||||
{
|
||||
return RefTagPattern.Replace(text, (match) => match.Groups["display"].Value);
|
||||
}
|
||||
private string HumanizeCodeTags(string text)
|
||||
{
|
||||
return CodeTagPattern.Replace(text, (match) => "{" + match.Groups["display"].Value + "}");
|
||||
}
|
||||
private string HumanizeParaTags(string text)
|
||||
{
|
||||
return ParaTagPattern.Replace(text, (match) => "<br>" + match.Groups["display"].Value);
|
||||
}
|
||||
private string DecodeXml(string text)
|
||||
{
|
||||
return System.Net.WebUtility.HtmlDecode(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MiddleWare", "MiddleWare",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.BackgroundTask", "MiddleWare\AntSK.BackgroundTask\AntSK.BackgroundTask.csproj", "{DF87E829-99C5-44A7-9718-B3E67DC801F6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.LLM", "AntSk.LLM\AntSK.LLM.csproj", "{19529BFA-152F-4A8C-8254-F2D4896AB739}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -36,6 +38,10 @@ Global
|
||||
{DF87E829-99C5-44A7-9718-B3E67DC801F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DF87E829-99C5-44A7-9718-B3E67DC801F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DF87E829-99C5-44A7-9718-B3E67DC801F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Downloader" Version="3.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -108,6 +108,13 @@
|
||||
<param name="fileid"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.plugins.Functions.FunctionTest.GetOrder(System.Int32)">
|
||||
<summary>
|
||||
获取订单信息
|
||||
</summary>
|
||||
<param name="id">订单号</param>
|
||||
<returns>订单信息</returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Services.LLamaSharp.LLamaChatService">
|
||||
<summary>
|
||||
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<CascadingValue Value="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" >
|
||||
<NotAuthorized>
|
||||
您没有权限访问此页面
|
||||
</NotAuthorized>
|
||||
<Authorizing>
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
|
||||
</Authorizing>
|
||||
</AuthorizeRouteView>
|
||||
</CascadingValue>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(BasicLayout)">
|
||||
<AntSK.Pages.Exception._404/>
|
||||
<AntSK.Pages.Exception._404 />
|
||||
</LayoutView>
|
||||
|
||||
</NotFound>
|
||||
</Router>
|
||||
<AntContainer />
|
||||
|
||||
26
src/AntSK/Components/ChartCard/ChartCard.razor
Normal file
26
src/AntSK/Components/ChartCard/ChartCard.razor
Normal file
@@ -0,0 +1,26 @@
|
||||
@namespace AntSK.Components
|
||||
@inherits AntDomComponentBase
|
||||
|
||||
<Card>
|
||||
<div class="chartCard">
|
||||
<div class="chartTop">
|
||||
<div class="avatar">@Avatar</div>
|
||||
<div class="metaWrap">
|
||||
<div class="meta">
|
||||
<span>@Title</span>
|
||||
<span class="action"><Icon Type="@Icon" /></span>
|
||||
</div>
|
||||
<div class="total">
|
||||
<span>@Total</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" style="height: @(string.IsNullOrEmpty(ContentHeight) ? "auto" : ContentHeight+"px")">
|
||||
<div class="@(string.IsNullOrEmpty(ContentHeight) ? "" : "contentFixed")">@ChildContent</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
@Footer
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
31
src/AntSK/Components/ChartCard/ChartCard.razor.cs
Normal file
31
src/AntSK/Components/ChartCard/ChartCard.razor.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Components
|
||||
{
|
||||
public partial class ChartCard
|
||||
{
|
||||
[Parameter]
|
||||
public string Avatar { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Action { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Total { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Footer { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string ContentHeight { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Icon { get; set; }
|
||||
}
|
||||
}
|
||||
77
src/AntSK/Components/ChartCard/ChartCard.razor.css
Normal file
77
src/AntSK/Components/ChartCard/ChartCard.razor.css
Normal file
@@ -0,0 +1,77 @@
|
||||
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable */
|
||||
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
|
||||
.chartCard {
|
||||
position: relative;
|
||||
}
|
||||
.chartCard .chartTop {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.chartCard .chartTopMargin {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.chartCard .chartTopHasMargin {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.chartCard .metaWrap {
|
||||
float: left;
|
||||
}
|
||||
.chartCard .avatar {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.chartCard .avatar img {
|
||||
border-radius: 100%;
|
||||
}
|
||||
.chartCard .meta {
|
||||
height: 22px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
.chartCard .action {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.chartCard .total {
|
||||
height: 38px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 30px;
|
||||
line-height: 38px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
}
|
||||
.chartCard .content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.chartCard .contentFixed {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.chartCard .footer {
|
||||
margin-top: 8px;
|
||||
padding-top: 9px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
.chartCard .footer > * {
|
||||
position: relative;
|
||||
}
|
||||
.chartCard .footerMargin {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
@inherits AntDomComponentBase
|
||||
|
||||
<Space Class="@ClassMapper.Class" Size="@("26")">
|
||||
<SpaceItem>
|
||||
<Image Src="https://img.shields.io/github/stars/xuzeyu91/antsk?style=social" Width="88px" Height="20px;"></Image>
|
||||
<SpaceItem Style="margin-left:20px;">
|
||||
<AvatarDropdown Name="@context.Identity.Name"
|
||||
Avatar="@_currentUser.Avatar"
|
||||
MenuItems="@AvatarMenuItems"
|
||||
|
||||
8
src/AntSK/Models/FileInfo.cs
Normal file
8
src/AntSK/Models/FileInfo.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace AntSK.Models
|
||||
{
|
||||
public class FileInfoModel
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -29,10 +29,7 @@
|
||||
<TextArea Placeholder="一个一行,以冒号分割。例如:Content-Type:application/json" @bind-Value="@context.Header" MinRows="3" />
|
||||
</FormItem>
|
||||
<FormItem Label="Method类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.Method">
|
||||
<Radio RadioButton Value="@(HttpMethodType.Get)">Get</Radio>
|
||||
<Radio RadioButton Value="@(HttpMethodType.Post)">Post</Radio>
|
||||
</RadioGroup>
|
||||
<EnumRadioGroup ButtonStyle="@RadioButtonStyle.Outline" @bind-Value="context.Method"></EnumRadioGroup>
|
||||
</FormItem>
|
||||
|
||||
@if (context.Method == HttpMethodType.Get)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@using AntSK.Domain.Model.Enum
|
||||
@page "/App/Add"
|
||||
@page "/App/Add/{AppId}"
|
||||
@using AntSK.Services.Auth
|
||||
@@ -12,8 +13,8 @@
|
||||
<Form Model="@_appModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="知识库名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入知识库名称" @bind-Value="@context.Name" />
|
||||
<FormItem Label="应用名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入应用名称" @bind-Value="@context.Name" />
|
||||
</FormItem>
|
||||
<FormItem Label="图标" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入图标" @bind-Value="@context.Icon" />
|
||||
@@ -21,8 +22,8 @@
|
||||
</FormItem>
|
||||
<FormItem Label="类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.Type">
|
||||
<Radio RadioButton Value="@("chat")">会话应用</Radio>
|
||||
<Radio RadioButton Value="@("kms")">知识库</Radio>
|
||||
<Radio RadioButton Value="@AppType.chat.ToString()">会话应用</Radio>
|
||||
<Radio RadioButton Value="@AppType.kms.ToString()">知识库</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
@@ -35,8 +36,9 @@
|
||||
ValueProperty="c=>c.Id"
|
||||
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
|
||||
</Select>
|
||||
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
|
||||
</FormItem>
|
||||
@if (@context.Type == "chat")
|
||||
@if (@context.Type == AppType.chat.ToString())
|
||||
{
|
||||
|
||||
<FormItem Label="提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
@@ -48,24 +50,40 @@
|
||||
<Slider TValue="double" Style="display: inline-block;width: 300px; " Min="0" Max="100" DefaultValue="70" @bind-Value="@context.Temperature" />
|
||||
<span>更发散</span>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="API插件列表(选择后会开启自动调用)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
|
||||
<FormItem Label="API插件列表" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select Mode="multiple"
|
||||
@bind-Values="apiIds"
|
||||
Placeholder="选择API插件"
|
||||
Placeholder="选择API插件, 选择后会开启自动调用"
|
||||
TItemValue="string"
|
||||
TItem="string"
|
||||
Size="@AntSizeLDSType.Default">
|
||||
<SelectOptions>
|
||||
@foreach (var api in _apiList)
|
||||
{
|
||||
<SelectOption TItem="string" TItemValue="string" Value="@api.Id" Label="@api.Name" />
|
||||
<SelectOption TItem="string" TItemValue="string" Value="@api.Id" Label="@(api.Name+"-"+api.Describe)" />
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="原生插件列表" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select Mode="multiple"
|
||||
@bind-Values="funIds"
|
||||
Placeholder="选择原生插件, 选择后会开启自动调用"
|
||||
TItemValue="string"
|
||||
TItem="string"
|
||||
Size="@AntSizeLDSType.Default">
|
||||
<SelectOptions>
|
||||
@foreach (var fun in _funList)
|
||||
{
|
||||
<SelectOption TItem="string" TItemValue="string" Value="@fun.Key" Label="@(fun.Key+"-"+fun.Value)" />
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</FormItem>
|
||||
}
|
||||
@if (@context.Type == "kms")
|
||||
@if (@context.Type == AppType.kms.ToString())
|
||||
{
|
||||
<FormItem Label="知识库" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select Mode="multiple"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Domain.Service;
|
||||
using AntSK.Domain.Model.Enum;
|
||||
using AntSK.Domain.Repositories;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace AntSK.Pages.AppPage
|
||||
{
|
||||
@@ -24,6 +26,8 @@ namespace AntSK.Pages.AppPage
|
||||
protected MessageService? Message { get; set; }
|
||||
[Inject]
|
||||
protected IAIModels_Repositories _aimodels_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected FunctionService _functionService { get; set; }
|
||||
|
||||
private Apps _appModel = new Apps();
|
||||
|
||||
@@ -31,10 +35,14 @@ namespace AntSK.Pages.AppPage
|
||||
|
||||
private List<Kmss> _kmsList = new List<Kmss>();
|
||||
|
||||
IEnumerable<string> apiIds;
|
||||
IEnumerable<string> apiIds=[];
|
||||
|
||||
private List<Apis> _apiList = new List<Apis>();
|
||||
|
||||
IEnumerable<string> funIds=[];
|
||||
|
||||
public Dictionary<string, string> _funList = new Dictionary<string, string>();
|
||||
|
||||
private List<AIModels> _chatList { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -44,6 +52,13 @@ namespace AntSK.Pages.AppPage
|
||||
_apiList = _apis_Repositories.GetList();
|
||||
|
||||
_chatList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Chat);
|
||||
_functionService.SearchMarkedMethods();
|
||||
foreach (var func in _functionService.Functions)
|
||||
{
|
||||
var methodInfo = _functionService.MethodInfos[func.Key];
|
||||
_funList.Add(func.Key, methodInfo.Description);
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(AppId))
|
||||
{
|
||||
@@ -51,6 +66,7 @@ namespace AntSK.Pages.AppPage
|
||||
_appModel = _apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
kmsIds = _appModel.KmsIdList?.Split(",");
|
||||
apiIds = _appModel.ApiFunctionList?.Split(",");
|
||||
funIds= _appModel.NativeFunctionList?.Split(",");
|
||||
}
|
||||
|
||||
|
||||
@@ -68,10 +84,10 @@ namespace AntSK.Pages.AppPage
|
||||
}
|
||||
_appModel.KmsIdList = string.Join(",", kmsIds);
|
||||
}
|
||||
if (apiIds != null && apiIds.Count() > 0)
|
||||
{
|
||||
_appModel.ApiFunctionList = string.Join(",", apiIds);
|
||||
}
|
||||
|
||||
_appModel.ApiFunctionList = string.Join(",", apiIds);
|
||||
|
||||
_appModel.NativeFunctionList = string.Join(",", funIds);
|
||||
|
||||
if (string.IsNullOrEmpty(AppId))
|
||||
{
|
||||
@@ -103,6 +119,11 @@ namespace AntSK.Pages.AppPage
|
||||
{
|
||||
NavigationManager.NavigateTo("/applist");
|
||||
}
|
||||
|
||||
private void NavigateModelList()
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/modellist");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Domain.Model.Enum
|
||||
@page "/AppList"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@@ -48,11 +49,11 @@
|
||||
<!--todo: Ellipsis not working-->
|
||||
@context.Describe
|
||||
</Paragraph>
|
||||
@if (context.Type == "chat")
|
||||
@if (context.Type == AppType.chat.ToString())
|
||||
{
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">会话应用</Tag>
|
||||
}
|
||||
else if (context.Type == "kms")
|
||||
else if (context.Type == AppType.kms.ToString())
|
||||
{
|
||||
<Tag Color="@PresetColor.Green.ToString()">知识库</Tag>
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Microsoft.KernelMemory;
|
||||
@@ -13,6 +12,7 @@ using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using RestSharp;
|
||||
using System.Text;
|
||||
using AntSK.Domain.Utils;
|
||||
using Markdig;
|
||||
|
||||
namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
@@ -41,6 +41,9 @@ namespace AntSK.Pages.ChatPage
|
||||
[Inject]
|
||||
IChatService _chatService { get; set; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<Chat> Logger { get; set; }
|
||||
|
||||
protected bool _loading = false;
|
||||
protected List<MessageInfo> MessageList = [];
|
||||
protected string? _messageInput;
|
||||
@@ -88,8 +91,8 @@ namespace AntSK.Pages.ChatPage
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Sendding = false;
|
||||
Console.WriteLine("异常:" + ex.Message);
|
||||
_ = Message.Error("异常:" + ex.Message, 2);
|
||||
Logger.LogError( ex,"对话异常");
|
||||
_ = Message.Error("异常:"+ex.Message, 2);
|
||||
}
|
||||
}
|
||||
protected async Task OnCopyAsync(MessageInfo item)
|
||||
@@ -155,7 +158,6 @@ namespace AntSK.Pages.ChatPage
|
||||
|
||||
|
||||
MessageInfo info = null;
|
||||
var markdown1 = new Markdown();
|
||||
var chatResult = _chatService.SendKmsByAppAsync(app, questions, msg, _relevantSources);
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
@@ -177,16 +179,12 @@ namespace AntSK.Pages.ChatPage
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown1.Transform(info.HtmlAnswers);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
|
||||
await MarkDown(info);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
@@ -197,7 +195,6 @@ namespace AntSK.Pages.ChatPage
|
||||
private async Task SendChat(string questions, string history, Apps app)
|
||||
{
|
||||
MessageInfo info =null;
|
||||
var markdown = new Markdown();
|
||||
var chatResult = _chatService.SendChatByAppAsync(app, questions, history);
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
@@ -219,16 +216,22 @@ namespace AntSK.Pages.ChatPage
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
|
||||
await MarkDown(info);
|
||||
}
|
||||
|
||||
|
||||
private async Task MarkDown(MessageInfo info)
|
||||
{
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
// info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
info!.HtmlAnswers = Markdown.ToHtml(info.HtmlAnswers);
|
||||
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await _JSRuntime.InvokeVoidAsync("Prism.highlightAll");
|
||||
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
|
||||
}
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,6 @@ using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.SemanticKernel;
|
||||
@@ -12,6 +11,7 @@ using SqlSugar;
|
||||
using System.Text;
|
||||
using AntSK.Domain.Utils;
|
||||
using Microsoft.JSInterop;
|
||||
using Markdig;
|
||||
|
||||
namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
@@ -152,7 +152,6 @@ namespace AntSK.Pages.ChatPage
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
MessageInfo info = null;
|
||||
var markdown1 = new Markdown();
|
||||
var chatResult=_chatService.SendKmsByAppAsync(app, questions, msg);
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
@@ -174,12 +173,7 @@ namespace AntSK.Pages.ChatPage
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown1.Transform(info.HtmlAnswers);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
|
||||
await MarkDown(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,7 +186,6 @@ namespace AntSK.Pages.ChatPage
|
||||
private async Task SendChat(string questions, string history, Apps app)
|
||||
{
|
||||
MessageInfo info = null;
|
||||
var markdown = new Markdown();
|
||||
var chatResult = _chatService.SendChatByAppAsync(app, questions, history);
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
@@ -214,11 +207,21 @@ namespace AntSK.Pages.ChatPage
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
|
||||
await MarkDown(info);
|
||||
}
|
||||
|
||||
private async Task MarkDown(MessageInfo info)
|
||||
{
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
// info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
info!.HtmlAnswers = Markdown.ToHtml(info.HtmlAnswers);
|
||||
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await _JSRuntime.InvokeVoidAsync("Prism.highlightAll");
|
||||
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
|
||||
}
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
|
||||
76
src/AntSK/Pages/Index.Razor.cs
Normal file
76
src/AntSK/Pages/Index.Razor.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using AntSK.Domain.Model.Enum;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages
|
||||
{
|
||||
public partial class Index
|
||||
{
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
|
||||
[Inject]
|
||||
IApps_Repositories _apps_Repositories { get; set; }
|
||||
[Inject]
|
||||
IAIModels_Repositories _aiModels_Repositories { get; set; }
|
||||
[Inject]
|
||||
IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
|
||||
|
||||
private string appCount;
|
||||
private string chatAppCount;
|
||||
private string kmsAppCount;
|
||||
|
||||
private string kmsCount;
|
||||
|
||||
private string fileCount;
|
||||
private string urlCount;
|
||||
private string textCount;
|
||||
private string filesCount;
|
||||
|
||||
private string chatAIModelCount;
|
||||
private string embeddingAIModelCount;
|
||||
private string aiModelCount;
|
||||
|
||||
private string imgSrc { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
chatAppCount = (await _apps_Repositories.CountAsync(p => p.Type == AppType.chat.ToString())).ConvertToString();
|
||||
kmsAppCount = (await _apps_Repositories.CountAsync(p => p.Type == AppType.kms.ToString())).ConvertToString();
|
||||
appCount= (chatAppCount.ConvertToInt32()+ kmsAppCount.ConvertToInt32()).ConvertToString();
|
||||
|
||||
kmsCount =(await _kmss_Repositories.CountAsync(p=>true)).ConvertToString();
|
||||
|
||||
chatAIModelCount = (await _aiModels_Repositories.CountAsync(p=>p.AIModelType==AIModelType.Chat)).ConvertToString();
|
||||
embeddingAIModelCount = (await _aiModels_Repositories.CountAsync(p=>p.AIModelType==AIModelType.Embedding)).ConvertToString();
|
||||
aiModelCount= (chatAIModelCount.ConvertToInt32() + embeddingAIModelCount.ConvertToInt32()).ConvertToString();
|
||||
|
||||
fileCount=(await _kmsDetails_Repositories.CountAsync(p =>p.Type=="file")).ConvertToString();
|
||||
urlCount = (await _kmsDetails_Repositories.CountAsync(p => p.Type == "url")).ConvertToString();
|
||||
textCount = (await _kmsDetails_Repositories.CountAsync(p => p.Type == "text")).ConvertToString();
|
||||
filesCount = (fileCount.ConvertToInt32() + urlCount.ConvertToInt32() + textCount.ConvertToInt32()).ConvertToString();
|
||||
|
||||
}
|
||||
|
||||
private void NavToApp()
|
||||
{
|
||||
NavigationManager.NavigateTo("/AppList");
|
||||
|
||||
}
|
||||
|
||||
private void NavToKms()
|
||||
{
|
||||
NavigationManager.NavigateTo("/KmsList");
|
||||
}
|
||||
|
||||
private void NavToAIModel()
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/modellist");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,71 @@
|
||||
@namespace AntSK.Pages
|
||||
@using MarkdownSharp
|
||||
@page "/"
|
||||
@using AntSK.Services.Auth
|
||||
@using AntSK.Components
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<Body>
|
||||
@((MarkupString)(body))
|
||||
<Image Width="200px" Src="@imgSrc" />
|
||||
</Body>
|
||||
<GridContent>
|
||||
<Row Type="flex" Gutter="24">
|
||||
<AntDesign.Col Xs="24" Sm="12" Md="12" Lg="12" Xl="6" Style="margin-bottom: 24px;">
|
||||
<div @onclick="NavToApp" style=" cursor: pointer;">
|
||||
<ChartCard Title="应用数量"
|
||||
Total="@appCount"
|
||||
Icon="wechat"
|
||||
ContentHeight="46">
|
||||
<ChildContent>
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">会话应用:@chatAppCount</Tag>
|
||||
<Tag Color="@PresetColor.Green.ToString()">知识库应用:@kmsAppCount</Tag>
|
||||
</ChildContent>
|
||||
<Footer>
|
||||
</Footer>
|
||||
</ChartCard>
|
||||
</div>
|
||||
</AntDesign.Col>
|
||||
<AntDesign.Col Xs="24" Sm="12" Md="12" Lg="12" Xl="6" Style="margin-bottom: 24px;">
|
||||
<div @onclick="NavToKms" style=" cursor: pointer;">
|
||||
<ChartCard Title="知识库数量"
|
||||
Total="@kmsCount"
|
||||
Icon="database"
|
||||
ContentHeight="46">
|
||||
<ChildContent>
|
||||
|
||||
@code {
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
private string imgSrc { get; set; }
|
||||
</ChildContent>
|
||||
<Footer>
|
||||
</Footer>
|
||||
</ChartCard>
|
||||
</div>
|
||||
</AntDesign.Col>
|
||||
<AntDesign.Col Xs="24" Sm="12" Md="12" Lg="12" Xl="6" Style="margin-bottom: 24px;">
|
||||
<div @onclick="NavToKms" style=" cursor: pointer;">
|
||||
<ChartCard Title="文档数量"
|
||||
Total="@filesCount"
|
||||
Icon="file"
|
||||
ContentHeight="46">
|
||||
<ChildContent>
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">文档:@fileCount</Tag>
|
||||
<Tag Color="@PresetColor.Green.ToString()">链接:@urlCount</Tag>
|
||||
<Tag Color="@PresetColor.Orange.ToString()">文本:@textCount</Tag>
|
||||
</ChildContent>
|
||||
<Footer>
|
||||
</Footer>
|
||||
</ChartCard>
|
||||
</div>
|
||||
</AntDesign.Col>
|
||||
<AntDesign.Col Xs="24" Sm="12" Md="12" Lg="12" Xl="6" Style="margin-bottom: 24px;">
|
||||
<div @onclick="NavToAIModel" style=" cursor: pointer;">
|
||||
<ChartCard Title="模型数量"
|
||||
Total="@aiModelCount"
|
||||
Icon="robot"
|
||||
ContentHeight="46">
|
||||
<ChildContent>
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">会话模型:@chatAIModelCount</Tag>
|
||||
<Tag Color="@PresetColor.Green.ToString()">向量模型:@embeddingAIModelCount</Tag>
|
||||
</ChildContent>
|
||||
<Footer>
|
||||
</Footer>
|
||||
</ChartCard>
|
||||
</div>
|
||||
</AntDesign.Col>
|
||||
</Row>
|
||||
</GridContent>
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
imgSrc = NavigationManager.BaseUri + "assets/gzh.png";
|
||||
}
|
||||
private string body = new Markdown().Transform(@"非常感谢您对**AntSK**的喜爱,您的支持对我来说是莫大的鼓励。
|
||||
|
||||
如果您对技术类视频感兴趣,欢迎关注我的B站账号,获取更多精彩内容。
|
||||
|
||||
- **Bilibili账号**:[点击这里](https://space.bilibili.com/1673184683/channel/series)
|
||||
|
||||
同时,如果您感兴趣,可以访问我的GitHub页面,查看更多开源项目和代码:
|
||||
|
||||
- **GitHub页面**:[点击这里](https://github.com/xuzeyu91)
|
||||
|
||||
另外,如果您想加入由我创建的**.Net/AI应用开发微信交流群**,欢迎先添加我的微信**(xuzeyu91)**。
|
||||
|
||||
发送“进群请求”,我将会把您邀请进群。
|
||||
|
||||
期待与您在线上交流技术,共同进步!
|
||||
|
||||
也欢迎您关注我的公众号,后面可以第一时间知道**AntSK**项目进展。");
|
||||
|
||||
|
||||
}
|
||||
|
||||
161
src/AntSK/Pages/Index.razor.css
Normal file
161
src/AntSK/Pages/Index.razor.css
Normal file
@@ -0,0 +1,161 @@
|
||||
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable */
|
||||
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
|
||||
.iconGroup span.anticon {
|
||||
margin-left: 16px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
cursor: pointer;
|
||||
transition: color 0.32s;
|
||||
}
|
||||
.iconGroup span.anticon:hover {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
.rankingList {
|
||||
margin: 25px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.rankingList li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
zoom: 1;
|
||||
}
|
||||
.rankingList li::before,
|
||||
.rankingList li::after {
|
||||
display: table;
|
||||
content: ' ';
|
||||
}
|
||||
.rankingList li::after {
|
||||
clear: both;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.rankingList li span {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
.rankingList li .rankingItemNumber {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: 1.5px;
|
||||
margin-right: 16px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.rankingList li .rankingItemNumber.active {
|
||||
color: #fff;
|
||||
background-color: #314659;
|
||||
}
|
||||
.rankingList li .rankingItemTitle {
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.salesExtra {
|
||||
display: inline-block;
|
||||
margin-right: 24px;
|
||||
}
|
||||
.salesExtra a {
|
||||
margin-left: 24px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
.salesExtra a:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
.salesExtra a.currentDate {
|
||||
color: #1890ff;
|
||||
}
|
||||
.salesCard .salesBar {
|
||||
padding: 0 0 32px 32px;
|
||||
}
|
||||
.salesCard .salesRank {
|
||||
padding: 0 32px 32px 72px;
|
||||
}
|
||||
.salesCard :global .ant-tabs-bar,
|
||||
.salesCard :global .ant-tabs-nav-wrap {
|
||||
padding-left: 16px;
|
||||
}
|
||||
.salesCard :global .ant-tabs-bar .ant-tabs-nav .ant-tabs-tab,
|
||||
.salesCard :global .ant-tabs-nav-wrap .ant-tabs-nav .ant-tabs-tab {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.salesCard :global .ant-tabs-extra-content {
|
||||
padding-right: 24px;
|
||||
line-height: 55px;
|
||||
}
|
||||
.salesCard :global .ant-card-head {
|
||||
position: relative;
|
||||
}
|
||||
.salesCard :global .ant-card-head-title {
|
||||
align-items: normal;
|
||||
}
|
||||
.salesCardExtra {
|
||||
height: inherit;
|
||||
}
|
||||
.salesTypeRadio {
|
||||
position: absolute;
|
||||
right: 54px;
|
||||
bottom: 12px;
|
||||
}
|
||||
.offlineCard :global .ant-tabs-ink-bar {
|
||||
bottom: auto;
|
||||
}
|
||||
.offlineCard :global .ant-tabs-bar {
|
||||
border-bottom: none;
|
||||
}
|
||||
.offlineCard :global .ant-tabs-nav-container-scrolling {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
.offlineCard :global .ant-tabs-tab-prev-icon::before {
|
||||
position: relative;
|
||||
left: 6px;
|
||||
}
|
||||
.offlineCard :global .ant-tabs-tab-next-icon::before {
|
||||
position: relative;
|
||||
right: 6px;
|
||||
}
|
||||
.offlineCard :global .ant-tabs-tab-active h4 {
|
||||
color: #1890ff;
|
||||
}
|
||||
.trendText {
|
||||
margin-left: 8px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
@media screen and (max-width: 992px) {
|
||||
.salesExtra {
|
||||
display: none;
|
||||
}
|
||||
.rankingList li span:first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.rankingTitle {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.salesCard .salesBar {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 576px) {
|
||||
.salesExtraWrap {
|
||||
display: none;
|
||||
}
|
||||
.salesCard :global .ant-tabs-content {
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@
|
||||
<PageContainer Title="新增知识库">
|
||||
<ChildContent>
|
||||
<Card>
|
||||
<Form
|
||||
Model="@_kmsModel"
|
||||
<Form Model="@_kmsModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="知识库名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
@@ -30,13 +29,15 @@
|
||||
ValueProperty="c=>c.Id"
|
||||
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+ c.ModelDescription">
|
||||
</Select>
|
||||
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
|
||||
</FormItem>
|
||||
<FormItem Label="嵌入模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<FormItem Label="向量模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select DataSource="@_embeddingList"
|
||||
@bind-Value="@context.EmbeddingModelID"
|
||||
ValueProperty="c=>c.Id"
|
||||
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
|
||||
</Select>
|
||||
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
|
||||
</FormItem>
|
||||
<FormItem Label="段落切片数(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<AntDesign.InputNumber @bind-Value="context.MaxTokensPerParagraph" PlaceHolder="段落切片数"></AntDesign.InputNumber>
|
||||
|
||||
@@ -28,8 +28,10 @@ namespace AntSK.Pages.KmsPage
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_chatList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Chat);
|
||||
_embeddingList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Embedding);
|
||||
//星火 Mock没实现KM先隐藏
|
||||
List<AIType> ignores = new List<AIType>() { AIType.SparkDesk, AIType.Mock };
|
||||
_chatList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Chat&& !ignores.Contains(p.AIType));
|
||||
_embeddingList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Embedding && !ignores.Contains(p.AIType));
|
||||
if (!string.IsNullOrEmpty(KmsId))
|
||||
{
|
||||
//查看
|
||||
@@ -68,5 +70,10 @@ namespace AntSK.Pages.KmsPage
|
||||
NavigationManager.NavigateTo("/kmslist");
|
||||
|
||||
}
|
||||
|
||||
private void NavigateModelList()
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/modellist");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,17 +140,7 @@
|
||||
Name="file"
|
||||
Drag
|
||||
Multiple
|
||||
Accept="text/plain,
|
||||
application/msword,
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
|
||||
application/vnd.ms-excel,
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,
|
||||
application/vnd.ms-powerpoint,
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
||||
application/pdf,
|
||||
application/json,
|
||||
text/markdown,
|
||||
text/x-markdown"
|
||||
Accept="*/*"
|
||||
BeforeUpload="BeforeUpload"
|
||||
OnSingleCompleted="OnSingleCompleted">
|
||||
<p class="ant-upload-drag-icon">
|
||||
|
||||
@@ -3,11 +3,14 @@ using AntSK.BackgroundTask;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using DocumentFormat.OpenXml.Drawing.Diagrams;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.KernelMemory;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static AntSK.Pages.KmsPage.KmsDetail;
|
||||
|
||||
namespace AntSK.Pages.KmsPage
|
||||
{
|
||||
@@ -27,8 +30,7 @@ namespace AntSK.Pages.KmsPage
|
||||
bool _textVisible = false;
|
||||
bool _textConfirmLoading = false;
|
||||
|
||||
string filePath;
|
||||
string fileName;
|
||||
List<FileInfoModel> fileList = new List<FileInfoModel>();
|
||||
|
||||
private Form<UrlModel> _urlForm;
|
||||
private UrlModel urlModel = new UrlModel();
|
||||
@@ -103,7 +105,8 @@ namespace AntSK.Pages.KmsPage
|
||||
});
|
||||
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
|
||||
_urlVisible = false;
|
||||
_ = _message.Info("加入队列,进入后台处理中!", 2);
|
||||
urlModel.Url = "";
|
||||
_ = _message.Info("加入队列,进入后台处理中!", 2);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
@@ -139,6 +142,7 @@ namespace AntSK.Pages.KmsPage
|
||||
});
|
||||
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
|
||||
_textVisible = false;
|
||||
textModel.Text = "";
|
||||
_ = _message.Info("加入队列,进入后台处理中!", 2);
|
||||
|
||||
}
|
||||
@@ -164,16 +168,20 @@ namespace AntSK.Pages.KmsPage
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _httpService.PostAsync(NavigationManager.BaseUri + "api/KMS/ImportKMSTask", new ImportKMSTaskDTO()
|
||||
foreach (var item in fileList)
|
||||
{
|
||||
ImportType = ImportType.File,
|
||||
KmsId = KmsId,
|
||||
FilePath = filePath,
|
||||
FileName = fileName
|
||||
});
|
||||
var result = await _httpService.PostAsync(NavigationManager.BaseUri + "api/KMS/ImportKMSTask", new ImportKMSTaskDTO()
|
||||
{
|
||||
ImportType = ImportType.File,
|
||||
KmsId = KmsId,
|
||||
FilePath = item.FilePath,
|
||||
FileName = item.FileName
|
||||
});
|
||||
}
|
||||
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
|
||||
//上传文档
|
||||
_fileVisible = false;
|
||||
fileList.Clear();
|
||||
_ = _message.Info("加入队列,进入后台处理中!", 2);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
@@ -220,11 +228,14 @@ namespace AntSK.Pages.KmsPage
|
||||
}
|
||||
private void OnSingleCompleted(UploadInfo fileinfo)
|
||||
{
|
||||
|
||||
if (fileinfo.File.State == UploadState.Success)
|
||||
{
|
||||
filePath = fileinfo.File.Url = fileinfo.File.Response;
|
||||
fileName = fileinfo.File.FileName;
|
||||
//文件列表
|
||||
fileList.Add(new FileInfoModel()
|
||||
{
|
||||
FileName = fileinfo.File.FileName,
|
||||
FilePath = fileinfo.File.Url = fileinfo.File.Response
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
@using AntSK.Domain.Model.Enum
|
||||
@page "/setting/model/add"
|
||||
@page "/setting/model/add/{ModelId}"
|
||||
@page "/setting/model/addbypath/{ModelPath}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
<PageContainer Title="新增模型">
|
||||
<ChildContent>
|
||||
@@ -19,26 +21,22 @@
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="AI类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.AIType">
|
||||
<Radio RadioButton Value="@(AIType.OpenAI)">OpenAI</Radio>
|
||||
<Radio RadioButton Value="@(AIType.AzureOpenAI)">AzureOpenAI</Radio>
|
||||
<Radio RadioButton Value="@(AIType.LLamaSharp)">LLamaSharp</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.AIModelType">
|
||||
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
|
||||
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
@if (context.AIModelType == AIModelType.Embedding)
|
||||
<EnumRadioGroup @bind-Value="context.AIType"></EnumRadioGroup>
|
||||
</FormItem>
|
||||
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.AIModelType">
|
||||
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
|
||||
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
@if (context.AIModelType == AIModelType.Embedding)
|
||||
{
|
||||
<FormItem Label="注意事项" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<b>请不要使用不同维度的向量模型,否则会导致无法向量存储</b>
|
||||
</FormItem>
|
||||
</FormItem>
|
||||
}
|
||||
|
||||
@if (context.AIType == AIType.AzureOpenAI)
|
||||
@if (context.AIType == AIType.AzureOpenAI)
|
||||
{
|
||||
<FormItem Label="请求地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入请求地址" @bind-Value="@context.EndPoint" />
|
||||
@@ -53,7 +51,7 @@
|
||||
@if (context.AIType == AIType.OpenAI)
|
||||
{
|
||||
<FormItem Label="请求地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入请求地址" @bind-Value="@context.EndPoint" />
|
||||
<Input Placeholder="请输入请求地址 示例格式 http://ip:port/" @bind-Value="@context.EndPoint" />
|
||||
</FormItem>
|
||||
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
|
||||
@@ -62,12 +60,31 @@
|
||||
<InputPassword @bind-Value="@context.ModelKey" Placeholder="请输入模型秘钥" Size="@InputSize.Large" />
|
||||
</FormItem>
|
||||
}
|
||||
@if (context.AIType == AIType.SparkDesk)
|
||||
{
|
||||
<FormItem Label="APPID" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入APPID" @bind-Value="@context.EndPoint" />
|
||||
</FormItem>
|
||||
<FormItem Label="APIKey" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入请输入APIKey" @bind-Value="@context.ModelName" />
|
||||
</FormItem>
|
||||
<FormItem Label="APISecret" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<InputPassword @bind-Value="@context.ModelKey" Placeholder="APISecret" Size="@InputSize.Large" />
|
||||
</FormItem>
|
||||
}
|
||||
@if (context.AIType == AIType.LLamaSharp)
|
||||
{
|
||||
<FormItem Label="模型路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
|
||||
<InputGroup>
|
||||
<AutoComplete Options="_modelFiles" Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
|
||||
<Button OnClick="()=>_downloadModalVisible=true">从Haggingface下载</Button>
|
||||
</InputGroup>
|
||||
|
||||
</FormItem>
|
||||
}
|
||||
@if (context.AIType == AIType.Mock)
|
||||
{
|
||||
}
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" OnClick="HandleSubmit">
|
||||
保存
|
||||
@@ -81,6 +98,20 @@
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
|
||||
@code {
|
||||
<Modal @ref="_modal" Visible="_downloadModalVisible" Footer="null" Closable Title="模型下载" OnCancel="OnCancel" DestroyOnClose>
|
||||
<Flex Gap="10" Vertical>
|
||||
<InputGroup>
|
||||
<Input Disabled="_downloadStarted" Placeholder="请输入下载地址" @bind-Value="_downloadUrl" Style="width:80%"></Input>
|
||||
@if (!_downloadStarted)
|
||||
{
|
||||
<Button OnClick="StartDownload">开始</Button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Button OnClick="Stop">停止</Button>
|
||||
}
|
||||
</InputGroup>
|
||||
<AntDesign.Progress Percent="_downloadProgress"></AntDesign.Progress>
|
||||
|
||||
}
|
||||
</Flex>
|
||||
</Modal>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using AntDesign;
|
||||
using AntDesign.ProLayout;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Downloader;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
@@ -10,21 +13,52 @@ namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
[Parameter]
|
||||
public string ModelId { get; set; }
|
||||
[Parameter]
|
||||
public string ModelPath { get; set; }
|
||||
[Inject] protected IAIModels_Repositories _aimodels_Repositories { get; set; }
|
||||
[Inject] protected MessageService? Message { get; set; }
|
||||
[Inject] public HttpClient HttpClient { get; set; }
|
||||
|
||||
private AIModels _aiModel = new AIModels();
|
||||
|
||||
private string _downloadUrl;
|
||||
private bool _downloadModalVisible;
|
||||
private double _downloadProgress;
|
||||
private bool _downloadFinished;
|
||||
private bool _downloadStarted;
|
||||
IDownload _download;
|
||||
|
||||
private Modal _modal;
|
||||
|
||||
string[] _modelFiles;
|
||||
|
||||
IEnumerable<string> _menuKeys;
|
||||
|
||||
private List<MenuDataItem> menuList = new List<MenuDataItem>();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (!string.IsNullOrEmpty(ModelId))
|
||||
try
|
||||
{
|
||||
_aiModel = _aimodels_Repositories.GetFirst(p => p.Id == ModelId);
|
||||
await base.OnInitializedAsync();
|
||||
if (!string.IsNullOrEmpty(ModelId))
|
||||
{
|
||||
_aiModel = _aimodels_Repositories.GetFirst(p => p.Id == ModelId);
|
||||
}
|
||||
//目前只支持gguf的 所以筛选一下
|
||||
_modelFiles = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory)).Where(p=>p.Contains(".gguf")).ToArray();
|
||||
if (!string.IsNullOrEmpty(ModelPath))
|
||||
{
|
||||
//下载页跳入
|
||||
_aiModel.AIType = Domain.Model.Enum.AIType.LLamaSharp;
|
||||
_downloadModalVisible = true;
|
||||
|
||||
_downloadUrl = $"https://hf-mirror.com{ModelPath.Replace("---","/")}";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_ = Message.Error("LLamaSharp.FileDirectory目录配置不正确!", 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,5 +103,68 @@ namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/modellist");
|
||||
}
|
||||
|
||||
private async Task StartDownload()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_downloadUrl))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_download = DownloadBuilder.New()
|
||||
.WithUrl(_downloadUrl)
|
||||
.WithDirectory(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory))
|
||||
.WithConfiguration(new DownloadConfiguration()
|
||||
{
|
||||
ParallelCount = 5,
|
||||
})
|
||||
.Build();
|
||||
|
||||
_download.DownloadProgressChanged += DownloadProgressChanged;
|
||||
_download.DownloadFileCompleted += DownloadFileCompleted;
|
||||
_download.DownloadStarted += DownloadStarted;
|
||||
|
||||
await _download.StartAsync();
|
||||
|
||||
//download.Stop(); // cancel current download
|
||||
}
|
||||
|
||||
private void DownloadProgressChanged(object? sender, DownloadProgressChangedEventArgs e)
|
||||
{
|
||||
_downloadProgress = Math.Round( e.ProgressPercentage,2);
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void DownloadFileCompleted(object? sender, AsyncCompletedEventArgs e)
|
||||
{
|
||||
_downloadFinished = true;
|
||||
_aiModel.ModelName = _download.Package.FileName;
|
||||
_downloadModalVisible = false;
|
||||
_downloadStarted = false;
|
||||
_modelFiles = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory));
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void DownloadStarted(object? sender, DownloadStartedEventArgs e)
|
||||
{
|
||||
_downloadStarted = true;
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void OnCancel()
|
||||
{
|
||||
if (_downloadStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_downloadModalVisible = false;
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
_downloadStarted=false;
|
||||
_download?.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,62 @@
|
||||
@namespace AntSK.Pages.Setting.AIModel
|
||||
@page "/setting/modeldown"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
<PageContainer Title="模型下载">
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using AntSK.Domain.Model.hfmirror
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
|
||||
<PageContainer Title="模型列表">
|
||||
<Content>
|
||||
<div style="text-align: center;">
|
||||
<Search Placeholder="输入回车"
|
||||
EnterButton="@("搜索")"
|
||||
Size="large"
|
||||
Style="max-width: 522px; width: 100%;"
|
||||
OnSearch="Search" />
|
||||
</div>
|
||||
</Content>
|
||||
<ChildContent>
|
||||
<h1>支持LLamaSharp的本地模型 支持gguf类型,推荐使用llama或者qwen</h1>
|
||||
<h1>如果模型加载报内存错误,可能是和llama.cpp版本不一致</h1>
|
||||
<a href="https://hf-mirror.com/models?search=gguf" target="_blank" rel="noopener noreferrer">打开下载地址</a>
|
||||
<div class="filterCardList">
|
||||
<AntList TItem="HfModels"
|
||||
Grid="LayoutModel._listGridType"
|
||||
DataSource="_modelList">
|
||||
<ListItem NoFlex>
|
||||
<Card Hoverable
|
||||
BodyStyle="padding-bottom: 20px;"
|
||||
Actions="new[] {
|
||||
down(()=> Down(context.Id))
|
||||
}">
|
||||
<CardMeta>
|
||||
<TitleTemplate>
|
||||
@context.Id
|
||||
</TitleTemplate>
|
||||
<AvatarTemplate>
|
||||
<Avatar Size="small" Src="@context.AuthorData.AvatarUrl" />
|
||||
</AvatarTemplate>
|
||||
</CardMeta>
|
||||
<div class="cardItemContent">
|
||||
<div class="cardInfo">
|
||||
<div>
|
||||
<p>Downloads</p>
|
||||
<p>@context.Downloads.ToString("0,0")</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Likes</p>
|
||||
<p>@context.Likes.ToString("0,0")</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</ListItem>
|
||||
</AntList>
|
||||
</div>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
@code {
|
||||
@code
|
||||
{
|
||||
RenderFragment down(Action clickAction) =>@<a key="down" @onclick="@clickAction">下载</a>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
52
src/AntSK/Pages/Setting/AIModel/ModelDown.razor.cs
Normal file
52
src/AntSK/Pages/Setting/AIModel/ModelDown.razor.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Model.hfmirror;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using AntSK.Domain.Utils;
|
||||
|
||||
namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
public partial class ModelDown
|
||||
{
|
||||
private readonly ListFormModel _model = new ListFormModel();
|
||||
private readonly IList<string> _selectCategories = new List<string>();
|
||||
|
||||
private List<HfModels> _modelList = new List<HfModels>();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
InitData("");
|
||||
}
|
||||
|
||||
private void InitData(string searchKey)
|
||||
{
|
||||
var param = searchKey.ConvertToString().Split(" ");
|
||||
|
||||
string urlBase = "https://hf-mirror.com/models-json?sort=trending&search=gguf";
|
||||
if (param.Count() > 0)
|
||||
{
|
||||
urlBase += "+" + string.Join("+", param);
|
||||
}
|
||||
RestClient client = new RestClient();
|
||||
RestRequest request = new RestRequest(urlBase, Method.Get);
|
||||
var response = client.Execute(request);
|
||||
var model = JsonConvert.DeserializeObject<HfModel>(response.Content);
|
||||
_modelList = model.models;
|
||||
}
|
||||
|
||||
private async Task Search(string searchKey)
|
||||
{
|
||||
InitData(searchKey);
|
||||
}
|
||||
|
||||
private void Down(string modelPath)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/setting/modeldown/detail/{modelPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/AntSK/Pages/Setting/AIModel/ModelDown.razor.css
Normal file
46
src/AntSK/Pages/Setting/AIModel/ModelDown.razor.css
Normal file
@@ -0,0 +1,46 @@
|
||||
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable */
|
||||
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
|
||||
.filterCardList .ant-card-meta-content {
|
||||
margin-top: 0;
|
||||
}
|
||||
.filterCardList .ant-card-meta-avatar {
|
||||
font-size: 0;
|
||||
}
|
||||
.filterCardList .ant-list .ant-list-item-content-single {
|
||||
max-width: 100%;
|
||||
}
|
||||
.filterCardList .cardInfo {
|
||||
margin-top: 16px;
|
||||
margin-left: 40px;
|
||||
zoom: 1;
|
||||
}
|
||||
.filterCardList .cardInfo::before,
|
||||
.filterCardList .cardInfo::after {
|
||||
display: table;
|
||||
content: ' ';
|
||||
}
|
||||
.filterCardList .cardInfo::after {
|
||||
clear: both;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.filterCardList .cardInfo > div {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 50%;
|
||||
text-align: left;
|
||||
}
|
||||
.filterCardList .cardInfo > div p {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
.filterCardList .cardInfo > div p:first-child {
|
||||
margin-bottom: 4px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
54
src/AntSK/Pages/Setting/AIModel/ModelDownDetail.razor
Normal file
54
src/AntSK/Pages/Setting/AIModel/ModelDownDetail.razor
Normal file
@@ -0,0 +1,54 @@
|
||||
@namespace AntSK.Pages.Setting.AIModel
|
||||
@page "/setting/modeldown/detail/{ModelName}/{ModelPath}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using AntSK.Domain.Model.hfmirror
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
|
||||
<div>
|
||||
<PageContainer Title="模型下载">
|
||||
<ChildContent>
|
||||
<div class="standardList">
|
||||
<Card Class="listCard"
|
||||
Title="模型列表"
|
||||
Style="margin-top: 24px;"
|
||||
BodyStyle="padding: 0 32px 40px 32px">
|
||||
<Extra>
|
||||
|
||||
</Extra>
|
||||
<ChildContent>
|
||||
<AntList TItem="HfModelDetail"
|
||||
DataSource="modelList"
|
||||
ItemLayout="ListItemLayout.Horizontal">
|
||||
<ListItem Actions="new[] {
|
||||
down(()=> Down(context.Path))
|
||||
}" Style="width:100%">
|
||||
<div class="listContent" style="width:100%">
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>名称</b>
|
||||
<p>@context.Name</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>文件大小</b>
|
||||
<p>@context.Size</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>更新时间</b>
|
||||
<p>@context.Time</p>
|
||||
</div>
|
||||
</div>
|
||||
</ListItem>
|
||||
</AntList>
|
||||
</ChildContent>
|
||||
</Card>
|
||||
</div>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
RenderFragment down(Action clickAction) =>@<a key="down" @onclick="@clickAction">下载</a>;
|
||||
}
|
||||
56
src/AntSK/Pages/Setting/AIModel/ModelDownDetail.razor.cs
Normal file
56
src/AntSK/Pages/Setting/AIModel/ModelDownDetail.razor.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using AntSK.Domain.Model.hfmirror;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using HtmlAgilityPack;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using RestSharp;
|
||||
|
||||
namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
public partial class ModelDownDetail
|
||||
{
|
||||
[Parameter]
|
||||
public string ModelName { get; set; }
|
||||
[Parameter]
|
||||
public string ModelPath { get; set; }
|
||||
|
||||
List<HfModelDetail> modelList = new List<HfModelDetail>();
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
InitData();
|
||||
}
|
||||
|
||||
private void InitData()
|
||||
{
|
||||
string urlBase = $"https://hf-mirror.com/{ModelName}/{ModelPath}/tree/main";
|
||||
RestClient client = new RestClient();
|
||||
RestRequest request = new RestRequest(urlBase, Method.Get);
|
||||
var response = client.Execute(request);
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(response.Content);
|
||||
|
||||
foreach (var listItem in htmlDocument.DocumentNode.SelectNodes("//li[contains(@class,'grid')]"))
|
||||
{
|
||||
var modelNameNode = listItem.SelectSingleNode(".//span[contains(@class,'truncate')]");
|
||||
var fileSizeNode = listItem.SelectSingleNode(".//a[contains(@class,'text-[0.8rem]')]");
|
||||
var downloadNode = listItem.SelectSingleNode(".//a[@title='Download file']");
|
||||
var timeNode = listItem.SelectSingleNode(".//time");
|
||||
|
||||
var modelName = modelNameNode?.InnerText.Trim();
|
||||
var fileSizeInfo = fileSizeNode?.InnerText.Trim().Split(' ');
|
||||
var fileSize = fileSizeInfo?.Length > 1 ? fileSizeInfo[fileSizeInfo.Length - 2] : null;
|
||||
var downloadUrl = downloadNode?.GetAttributeValue("href", null)?.Trim();
|
||||
var time= timeNode?.InnerText.Trim();
|
||||
|
||||
modelList.Add(new HfModelDetail() { Name = modelName, Size = string.Join(" ",fileSizeInfo), Path = downloadUrl,Time=time });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void Down(string path)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/setting/model/addbypath/{path.Replace("?download=true", "").Replace("/", "---")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
186
src/AntSK/Pages/Setting/AIModel/ModelDownDetail.razor.css
Normal file
186
src/AntSK/Pages/Setting/AIModel/ModelDownDetail.razor.css
Normal file
@@ -0,0 +1,186 @@
|
||||
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable */
|
||||
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
|
||||
.standardList .ant-card-head {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.standardList .ant-card-head-title {
|
||||
padding: 24px 0;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.standardList .ant-card-extra {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.standardList .ant-list-pagination {
|
||||
margin-top: 24px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.standardList .ant-avatar-lg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > span {
|
||||
display: inline-block;
|
||||
margin-bottom: 4px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > p {
|
||||
margin: 0;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > em {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 56px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.standardList .listContent {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.standardList .listContent .listContentItem {
|
||||
display: inline-block;
|
||||
margin-left: 40px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.standardList .listContent .listContentItem > span {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.standardList .listContent .listContentItem > p {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.standardList .extraContentSearch {
|
||||
width: 272px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.standardList .ant-list-item-content {
|
||||
display: block;
|
||||
flex: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.standardList .ant-list-item-action {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .listContent {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .listContent > div {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .listCard .ant-card-head-title {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.standardList .extraContentSearch {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .headerInfo {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > em {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.standardList .listContent > div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.listCard .ant-radio-group {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) and (min-width: 768px) {
|
||||
.standardList .listContent > div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.standardList .listContent > div {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1400px) {
|
||||
.standardList .listContent {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.standardListForm .ant-form-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.standardListForm .ant-form-item:last-child {
|
||||
margin-bottom: 32px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.formResult {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.formResult [class^='title'] {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
<div>
|
||||
<PageContainer Title="模型管理">
|
||||
@@ -56,6 +58,14 @@
|
||||
{
|
||||
<Tag Color="@PresetColor.Red.ToString()">LLamaSharp</Tag>
|
||||
}
|
||||
else if (context.AIType == AIType.SparkDesk)
|
||||
{
|
||||
<Tag Color="@PresetColor.Orange.ToString()">SparkDesk</Tag>
|
||||
}
|
||||
else if (context.AIType == AIType.Mock)
|
||||
{
|
||||
<Tag Color="@PresetColor.Cyan.ToString()">Mock</Tag>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
@page "/setting/user/add/{UserId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
<PageContainer Title="新增用户">
|
||||
<ChildContent>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
@inject IMessageService _message
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
|
||||
<PageContainer Title="个人设置">
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@attribute [Authorize(Roles = "AntSKAdmin")]
|
||||
|
||||
<div>
|
||||
<PageContainer Title="用户管理">
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
<link href="_content/AntDesign.ProLayout/css/ant-design-pro-layout-blazor.css" rel="stylesheet" />
|
||||
<link href="./css/site.css" rel="stylesheet" />
|
||||
<link href="AntSK.styles.css" rel="stylesheet" />
|
||||
|
||||
<link href="./css/normalize.css" rel="stylesheet" />
|
||||
<link href="./css/prism-coy.min.css" rel="stylesheet" />
|
||||
<link href="./css/prism-toolbar.min.css" rel="stylesheet" />
|
||||
<link href="./css/prism-line-numbers.min.css" rel="stylesheet" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<app>
|
||||
@@ -27,5 +33,19 @@
|
||||
<script src="_content/AntDesign.Charts/ant-design-charts-blazor.js"></script>
|
||||
<script src="_framework/blazor.server.js"></script>
|
||||
<script src="./js/scrollHelper.js"></script>
|
||||
|
||||
|
||||
<!--prism核心js (用于渲染代码块)-->
|
||||
<script src="./js/prism.min.js"></script>
|
||||
<!--显示代码块行号-->
|
||||
<script src="./js/prism-line-numbers.min.js"></script>
|
||||
<!--工具栏(一些插件的前置依赖)-->
|
||||
<script src="./js/prism-toolbar.min.js"></script>
|
||||
<!--代码块显示语言名称-->
|
||||
<script src="./js/prism-show-language.min.js"></script>
|
||||
<!--复制代码-->
|
||||
<script src="./js/prism-copy-to-clipboard.min.js"></script>
|
||||
<!--自动去cdn加载对应语言的代码高亮js-->
|
||||
<script src="./js/prism-autoloader.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,10 +2,12 @@ using AntDesign.ProLayout;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Common.Map;
|
||||
using AntSK.Domain.Domain.Other;
|
||||
using AntSK.Domain.Domain.Service;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.plugins.Functions;
|
||||
using AntSK.Services.Auth;
|
||||
using LLama.Native;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -32,6 +34,7 @@ builder.Services.AddServerSideBlazor();
|
||||
builder.Services.AddAntDesign();
|
||||
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddCascadingAuthenticationState();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, AntSKAuthProvider>();
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient
|
||||
@@ -41,6 +44,8 @@ builder.Services.AddScoped(sp => new HttpClient
|
||||
builder.Services.Configure<ProSettings>(builder.Configuration.GetSection("ProSettings"));
|
||||
builder.Services.AddServicesFromAssemblies("AntSK");
|
||||
builder.Services.AddServicesFromAssemblies("AntSK.Domain");
|
||||
builder.Services.AddSingleton(sp => new FunctionService(sp, [typeof(AntSK.App).Assembly, typeof(AntSK.Domain.Common.AntSkFunctionAttribute).Assembly]));
|
||||
builder.Services.AddSingleton<FunctionTest>();
|
||||
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
@@ -138,6 +143,4 @@ void InitDB(WebApplication app)
|
||||
//创建vector插件如果数据库没有则需要提供支持向量的数据库
|
||||
_repository.GetDB().Ado.ExecuteCommandAsync($"CREATE EXTENSION IF NOT EXISTS vector;");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -22,15 +22,20 @@ namespace AntSK.Services.Auth
|
||||
var user = _users_Repositories.GetFirst(p => p.No == username);
|
||||
if (username == LoginOption.User && password == LoginOption.Password)
|
||||
{
|
||||
string AdminRole = "AntSKAdmin";
|
||||
// 管理员认证成功,创建用户的ClaimsIdentity
|
||||
var claims = new[] { new Claim(ClaimTypes.Name, username) };
|
||||
identity = new ClaimsIdentity(claims, "AntSKAdmin");
|
||||
await _protectedSessionStore.SetAsync("UserSession", new UserSession() { UserName = username, Role = "AntSKAdmin" });
|
||||
var claims = new[] {
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
new Claim(ClaimTypes.Role, AdminRole)
|
||||
};
|
||||
identity = new ClaimsIdentity(claims, AdminRole);
|
||||
await _protectedSessionStore.SetAsync("UserSession", new UserSession() { UserName = username, Role = AdminRole });
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
string UserRole = "AntSKUser";
|
||||
if (user.IsNull())
|
||||
{
|
||||
return false;
|
||||
@@ -40,9 +45,12 @@ namespace AntSK.Services.Auth
|
||||
return false;
|
||||
}
|
||||
// 用户认证成功,创建用户的ClaimsIdentity
|
||||
var claims = new[] { new Claim(ClaimTypes.Name, username) };
|
||||
identity = new ClaimsIdentity(claims, "AntSKUser");
|
||||
await _protectedSessionStore.SetAsync("UserSession", new UserSession() { UserName = username, Role = "AntSKUser" });
|
||||
var claims = new[] {
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
new Claim(ClaimTypes.Role, UserRole)
|
||||
};
|
||||
identity = new ClaimsIdentity(claims, UserRole);
|
||||
await _protectedSessionStore.SetAsync("UserSession", new UserSession() { UserName = username, Role = UserRole });
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
return true;
|
||||
}
|
||||
@@ -50,24 +58,20 @@ namespace AntSK.Services.Auth
|
||||
|
||||
public ClaimsPrincipal GetCurrentUser()
|
||||
{
|
||||
//var userSessionStorageResult =await _protectedSessionStore.GetAsync<UserSession>("UserSession");
|
||||
//var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
|
||||
//if (userSession.IsNotNull())
|
||||
//{
|
||||
// var claims = new[] { new Claim(ClaimTypes.Name, userSession.UserName) };
|
||||
// identity = new ClaimsIdentity(claims, "AntSKUser");
|
||||
//}
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
return user;
|
||||
}
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
|
||||
var userSessionStorageResult = await _protectedSessionStore.GetAsync<UserSession>("UserSession");
|
||||
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
|
||||
if (userSession.IsNotNull())
|
||||
{
|
||||
var claims = new[] { new Claim(ClaimTypes.Name, userSession.UserName) };
|
||||
identity = new ClaimsIdentity(claims, "AntSKUser");
|
||||
var claims = new[] {
|
||||
new Claim(ClaimTypes.Name, userSession.UserName),
|
||||
new Claim( ClaimTypes.Role, userSession.Role) };
|
||||
identity = new ClaimsIdentity(claims, userSession.Role);
|
||||
}
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
return await Task.FromResult(new AuthenticationState(user));
|
||||
@@ -8,6 +8,7 @@ using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services.OpenApi
|
||||
@@ -29,7 +30,11 @@ namespace AntSK.Services.OpenApi
|
||||
{
|
||||
public async Task Chat(OpenAIModel model, string sk, HttpContext HttpContext)
|
||||
{
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == sk);
|
||||
string headerValue = sk;
|
||||
Regex regex = new Regex(@"Bearer (.*)");
|
||||
Match match = regex.Match(headerValue);
|
||||
string token = match.Groups[1].Value;
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == token);
|
||||
if (app.IsNotNull())
|
||||
{
|
||||
string msg = await HistorySummarize(app, model);
|
||||
@@ -59,6 +64,7 @@ namespace AntSK.Services.OpenApi
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
break;
|
||||
|
||||
case "kms":
|
||||
//知识库问答
|
||||
if (model.stream)
|
||||
@@ -71,7 +77,7 @@ namespace AntSK.Services.OpenApi
|
||||
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result3));
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
OpenAIResult result4 = new OpenAIResult();
|
||||
result4.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
@@ -84,7 +90,6 @@ namespace AntSK.Services.OpenApi
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task SendChatStream(HttpContext HttpContext, OpenAIStreamResult result, Apps app, string msg)
|
||||
@@ -126,11 +131,10 @@ namespace AntSK.Services.OpenApi
|
||||
var _kernel = _kernelService.GetKernelByApp(app);
|
||||
var temperature = app.Temperature / 100;//存的是0~100需要缩小
|
||||
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
|
||||
if (!string.IsNullOrEmpty(app.ApiFunctionList))
|
||||
{
|
||||
_kernelService.ImportFunctionsByApp(app, _kernel);
|
||||
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
|
||||
}
|
||||
|
||||
_kernelService.ImportFunctionsByApp(app, _kernel);
|
||||
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
|
||||
|
||||
var promptTemplateFactory = new KernelPromptTemplateFactory();
|
||||
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
|
||||
|
||||
@@ -208,6 +212,7 @@ namespace AntSK.Services.OpenApi
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
@@ -238,4 +243,4 @@ namespace AntSK.Services.OpenApi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,10 @@
|
||||
"TableNamePrefix": "km-"
|
||||
},
|
||||
"LLamaSharp": {
|
||||
"RunType": "GPU",
|
||||
"RunType": "GPU",
|
||||
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
|
||||
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
|
||||
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
|
||||
"FileDirectory": "./llama_models"
|
||||
},
|
||||
"Login": {
|
||||
"User": "admin",
|
||||
|
||||
24
src/AntSK/plugins/Functions/FunctionTest.cs
Normal file
24
src/AntSK/plugins/Functions/FunctionTest.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using AntSK.Domain.Common;
|
||||
|
||||
namespace AntSK.plugins.Functions
|
||||
{
|
||||
public class FunctionTest
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取订单信息
|
||||
/// </summary>
|
||||
/// <param name="id">订单号</param>
|
||||
/// <returns>订单信息</returns>
|
||||
[AntSkFunction]
|
||||
public string GetOrder(int id)
|
||||
{
|
||||
return $"""
|
||||
订单ID: {id}
|
||||
商品名:小米MIX4
|
||||
数量:1个
|
||||
价格:4999元
|
||||
收货地址:上海市黄浦区
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
349
src/AntSK/wwwroot/css/normalize.css
vendored
Normal file
349
src/AntSK/wwwroot/css/normalize.css
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
1
src/AntSK/wwwroot/css/prism-coy.min.css
vendored
Normal file
1
src/AntSK/wwwroot/css/prism-coy.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{position:relative;margin:.5em 0;overflow:visible;padding:1px}pre[class*=language-]>code{position:relative;z-index:1;border-left:10px solid #358ccb;box-shadow:-1px 0 0 0 #358ccb,0 0 0 1px #dfdfdf;background-color:#fdfdfd;background-image:linear-gradient(transparent 50%,rgba(69,142,209,.04) 50%);background-size:3em 3em;background-origin:content-box;background-attachment:local}code[class*=language-]{max-height:inherit;height:inherit;padding:0 1em;display:block;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background-color:#fdfdfd;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin-bottom:1em}:not(pre)>code[class*=language-]{position:relative;padding:.2em;border-radius:.3em;color:#c92c2c;border:1px solid rgba(0,0,0,.1);display:inline;white-space:normal}pre[class*=language-]:after,pre[class*=language-]:before{content:'';display:block;position:absolute;bottom:.75em;left:.18em;width:40%;height:20%;max-height:13em;box-shadow:0 13px 8px #979797;-webkit-transform:rotate(-2deg);-moz-transform:rotate(-2deg);-ms-transform:rotate(-2deg);-o-transform:rotate(-2deg);transform:rotate(-2deg)}pre[class*=language-]:after{right:.75em;left:auto;-webkit-transform:rotate(2deg);-moz-transform:rotate(2deg);-ms-transform:rotate(2deg);-o-transform:rotate(2deg);transform:rotate(2deg)}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#7d8b99}.token.punctuation{color:#5f6364}.token.boolean,.token.constant,.token.deleted,.token.function-name,.token.number,.token.property,.token.symbol,.token.tag{color:#c92c2c}.token.attr-name,.token.builtin,.token.char,.token.function,.token.inserted,.token.selector,.token.string{color:#2f9c0a}.token.entity,.token.operator,.token.url,.token.variable{color:#a67f59;background:rgba(255,255,255,.5)}.token.atrule,.token.attr-value,.token.class-name,.token.keyword{color:#1990b8}.token.important,.token.regex{color:#e90}.language-css .token.string,.style .token.string{color:#a67f59;background:rgba(255,255,255,.5)}.token.important{font-weight:400}.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.namespace{opacity:.7}@media screen and (max-width:767px){pre[class*=language-]:after,pre[class*=language-]:before{bottom:14px;box-shadow:none}}pre[class*=language-].line-numbers.line-numbers{padding-left:0}pre[class*=language-].line-numbers.line-numbers code{padding-left:3.8em}pre[class*=language-].line-numbers.line-numbers .line-numbers-rows{left:0}pre[class*=language-][data-line]{padding-top:0;padding-bottom:0;padding-left:0}pre[data-line] code{position:relative;padding-left:4em}pre .line-highlight{margin-top:0}
|
||||
1
src/AntSK/wwwroot/css/prism-line-numbers.min.css
vendored
Normal file
1
src/AntSK/wwwroot/css/prism-line-numbers.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
|
||||
1
src/AntSK/wwwroot/css/prism-toolbar.min.css
vendored
Normal file
1
src/AntSK/wwwroot/css/prism-toolbar.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none}
|
||||
1
src/AntSK/wwwroot/js/prism-autoloader.min.js
vendored
Normal file
1
src/AntSK/wwwroot/js/prism-autoloader.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/AntSK/wwwroot/js/prism-copy-to-clipboard.min.js
vendored
Normal file
1
src/AntSK/wwwroot/js/prism-copy-to-clipboard.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(){function u(t,e){t.addEventListener("click",function(){!function(t){navigator.clipboard?navigator.clipboard.writeText(t.getText()).then(t.success,function(){o(t)}):o(t)}(e)})}function o(e){var t=document.createElement("textarea");t.value=e.getText(),t.style.top="0",t.style.left="0",t.style.position="fixed",document.body.appendChild(t),t.focus(),t.select();try{var o=document.execCommand("copy");setTimeout(function(){o?e.success():e.error()},1)}catch(t){setTimeout(function(){e.error(t)},1)}document.body.removeChild(t)}"undefined"!=typeof Prism&&"undefined"!=typeof document&&(Prism.plugins.toolbar?Prism.plugins.toolbar.registerButton("copy-to-clipboard",function(t){var e=t.element,o=function(t){var e={copy:"Copy","copy-error":"Press Ctrl+C to copy","copy-success":"Copied!","copy-timeout":5e3};for(var o in e){for(var n="data-prismjs-"+o,c=t;c&&!c.hasAttribute(n);)c=c.parentElement;c&&(e[o]=c.getAttribute(n))}return e}(e),n=document.createElement("button");n.className="copy-to-clipboard-button",n.setAttribute("type","button");var c=document.createElement("span");return n.appendChild(c),i("copy"),u(n,{getText:function(){return e.textContent},success:function(){i("copy-success"),r()},error:function(){i("copy-error"),setTimeout(function(){!function(t){window.getSelection().selectAllChildren(t)}(e)},1),r()}}),n;function r(){setTimeout(function(){i("copy")},o["copy-timeout"])}function i(t){c.textContent=o[t],n.setAttribute("data-copy-state",t)}}):console.warn("Copy to Clipboard plugin loaded before Toolbar plugin."))}();
|
||||
1
src/AntSK/wwwroot/js/prism-line-numbers.min.js
vendored
Normal file
1
src/AntSK/wwwroot/js/prism-line-numbers.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var o="line-numbers",a=/\n(?!$)/g,e=Prism.plugins.lineNumbers={getLine:function(e,n){if("PRE"===e.tagName&&e.classList.contains(o)){var t=e.querySelector(".line-numbers-rows");if(t){var i=parseInt(e.getAttribute("data-start"),10)||1,r=i+(t.children.length-1);n<i&&(n=i),r<n&&(n=r);var s=n-i;return t.children[s]}}},resize:function(e){u([e])},assumeViewportIndependence:!0},n=void 0;window.addEventListener("resize",function(){e.assumeViewportIndependence&&n===window.innerWidth||(n=window.innerWidth,u(Array.prototype.slice.call(document.querySelectorAll("pre."+o))))}),Prism.hooks.add("complete",function(e){if(e.code){var n=e.element,t=n.parentNode;if(t&&/pre/i.test(t.nodeName)&&!n.querySelector(".line-numbers-rows")&&Prism.util.isActive(n,o)){n.classList.remove(o),t.classList.add(o);var i,r=e.code.match(a),s=r?r.length+1:1,l=new Array(s+1).join("<span></span>");(i=document.createElement("span")).setAttribute("aria-hidden","true"),i.className="line-numbers-rows",i.innerHTML=l,t.hasAttribute("data-start")&&(t.style.counterReset="linenumber "+(parseInt(t.getAttribute("data-start"),10)-1)),e.element.appendChild(i),u([t]),Prism.hooks.run("line-numbers",e)}}}),Prism.hooks.add("line-numbers",function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0})}function u(e){if(0!=(e=e.filter(function(e){var n=function(e){return e?window.getComputedStyle?getComputedStyle(e):e.currentStyle||null:null}(e)["white-space"];return"pre-wrap"===n||"pre-line"===n})).length){var n=e.map(function(e){var n=e.querySelector("code"),t=e.querySelector(".line-numbers-rows");if(n&&t){var i=e.querySelector(".line-numbers-sizer"),r=n.textContent.split(a);i||((i=document.createElement("span")).className="line-numbers-sizer",n.appendChild(i)),i.innerHTML="0",i.style.display="block";var s=i.getBoundingClientRect().height;return i.innerHTML="",{element:e,lines:r,lineHeights:[],oneLinerHeight:s,sizer:i}}}).filter(Boolean);n.forEach(function(e){var i=e.sizer,n=e.lines,r=e.lineHeights,s=e.oneLinerHeight;r[n.length-1]=void 0,n.forEach(function(e,n){if(e&&1<e.length){var t=i.appendChild(document.createElement("span"));t.style.display="block",t.textContent=e}else r[n]=s})}),n.forEach(function(e){for(var n=e.sizer,t=e.lineHeights,i=0,r=0;r<t.length;r++)void 0===t[r]&&(t[r]=n.children[i++].getBoundingClientRect().height)}),n.forEach(function(e){var n=e.sizer,t=e.element.querySelector(".line-numbers-rows");n.style.display="none",n.innerHTML="",e.lineHeights.forEach(function(e,n){t.children[n].style.height=e+"px"})})}}}();
|
||||
1
src/AntSK/wwwroot/js/prism-show-language.min.js
vendored
Normal file
1
src/AntSK/wwwroot/js/prism-show-language.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/AntSK/wwwroot/js/prism-toolbar.min.js
vendored
Normal file
1
src/AntSK/wwwroot/js/prism-toolbar.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var i=[],l={},d=function(){};Prism.plugins.toolbar={};var e=Prism.plugins.toolbar.registerButton=function(e,n){var t;t="function"==typeof n?n:function(e){var t;return"function"==typeof n.onClick?((t=document.createElement("button")).type="button",t.addEventListener("click",function(){n.onClick.call(this,e)})):"string"==typeof n.url?(t=document.createElement("a")).href=n.url:t=document.createElement("span"),n.className&&t.classList.add(n.className),t.textContent=n.text,t},e in l?console.warn('There is a button with the key "'+e+'" registered already.'):i.push(l[e]=t)},t=Prism.plugins.toolbar.hook=function(a){var e=a.element.parentNode;if(e&&/pre/i.test(e.nodeName)&&!e.parentNode.classList.contains("code-toolbar")){var t=document.createElement("div");t.classList.add("code-toolbar"),e.parentNode.insertBefore(t,e),t.appendChild(e);var r=document.createElement("div");r.classList.add("toolbar");var n=i,o=function(e){for(;e;){var t=e.getAttribute("data-toolbar-order");if(null!=t)return(t=t.trim()).length?t.split(/\s*,\s*/g):[];e=e.parentElement}}(a.element);o&&(n=o.map(function(e){return l[e]||d})),n.forEach(function(e){var t=e(a);if(t){var n=document.createElement("div");n.classList.add("toolbar-item"),n.appendChild(t),r.appendChild(n)}}),t.appendChild(r)}};e("label",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-label")){var n,a,r=t.getAttribute("data-label");try{a=document.querySelector("template#"+r)}catch(e){}return a?n=a.content:(t.hasAttribute("data-url")?(n=document.createElement("a")).href=t.getAttribute("data-url"):n=document.createElement("span"),n.textContent=r),n}}),Prism.hooks.add("complete",t)}}();
|
||||
16
src/AntSK/wwwroot/js/prism.min.js
vendored
Normal file
16
src/AntSK/wwwroot/js/prism.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
14
src/AntSk.LLM/AntSK.LLM.csproj
Normal file
14
src/AntSk.LLM/AntSK.LLM.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.4.0" />
|
||||
<PackageReference Include="Sdcb.SparkDesk" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
56
src/AntSk.LLM/Mock/MockTextCompletion.cs
Normal file
56
src/AntSk.LLM/Mock/MockTextCompletion.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using AntSK.LLM.SparkDesk;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Microsoft.SemanticKernel.Services;
|
||||
using Microsoft.SemanticKernel.TextGeneration;
|
||||
using Sdcb.SparkDesk;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Unicode;
|
||||
|
||||
namespace AntSK.LLM.Mock
|
||||
{
|
||||
public class MockTextCompletion : ITextGenerationService, IAIService
|
||||
{
|
||||
private readonly Dictionary<string, object?> _attributes = new();
|
||||
private readonly SparkDeskClient _client;
|
||||
private string _chatId;
|
||||
private readonly SparkDeskOptions _options;
|
||||
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
|
||||
};
|
||||
|
||||
public IReadOnlyDictionary<string, object?> Attributes => _attributes;
|
||||
|
||||
public MockTextCompletion()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<TextContent>> GetTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
string result = $"这是一条Mock数据,便于聊天测试,你的消息是:{prompt}";
|
||||
return [new(result.ToString())];
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<StreamingTextContent> GetStreamingTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
string result = $"这是一条Mock数据,便于聊天测试,你的消息是:{prompt}";
|
||||
foreach (var c in result)
|
||||
{
|
||||
var streamingTextContent = new StreamingTextContent(c.ToString(), modelId: "mock");
|
||||
|
||||
yield return streamingTextContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/AntSk.LLM/SparkDesk/SparkDeskOptions.cs
Normal file
20
src/AntSk.LLM/SparkDesk/SparkDeskOptions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Sdcb.SparkDesk;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.LLM.SparkDesk
|
||||
{
|
||||
public class SparkDeskOptions
|
||||
{
|
||||
public string AppId { get; set; }
|
||||
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
public string ApiSecret { get; set; }
|
||||
|
||||
public ModelVersion ModelVersion { get; set; }
|
||||
}
|
||||
}
|
||||
195
src/AntSk.LLM/SparkDesk/SparkDeskTextCompletion.cs
Normal file
195
src/AntSk.LLM/SparkDesk/SparkDeskTextCompletion.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Microsoft.SemanticKernel.Services;
|
||||
using Microsoft.SemanticKernel.TextGeneration;
|
||||
using Sdcb.SparkDesk;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Unicode;
|
||||
|
||||
namespace AntSK.LLM.SparkDesk
|
||||
{
|
||||
public class SparkDeskTextCompletion : ITextGenerationService, IAIService
|
||||
{
|
||||
private readonly Dictionary<string, object?> _attributes = new();
|
||||
private readonly SparkDeskClient _client;
|
||||
private string _chatId;
|
||||
private readonly SparkDeskOptions _options;
|
||||
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
|
||||
};
|
||||
|
||||
public IReadOnlyDictionary<string, object?> Attributes => _attributes;
|
||||
|
||||
public SparkDeskTextCompletion(SparkDeskOptions options, string chatId)
|
||||
{
|
||||
_options = options;
|
||||
_chatId = chatId;
|
||||
_client = new(options.AppId, options.ApiKey, options.ApiSecret);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<TextContent>> GetTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
var parameters = new ChatRequestParameters
|
||||
{
|
||||
ChatId = _chatId,
|
||||
};
|
||||
|
||||
OpenAIPromptExecutionSettings chatExecutionSettings = OpenAIPromptExecutionSettings.FromExecutionSettings(executionSettings);
|
||||
|
||||
parameters.Temperature = (float)chatExecutionSettings.Temperature;
|
||||
parameters.MaxTokens = chatExecutionSettings.MaxTokens ?? parameters.MaxTokens;
|
||||
|
||||
await foreach (StreamedChatResponse msg in _client.ChatAsStreamAsync(_options.ModelVersion, GetHistories(prompt), parameters))
|
||||
{
|
||||
sb.Append(msg);
|
||||
};
|
||||
|
||||
return [new(sb.ToString())];
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<StreamingTextContent> GetStreamingTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var parameters = new ChatRequestParameters
|
||||
{
|
||||
ChatId = _chatId,
|
||||
};
|
||||
OpenAIPromptExecutionSettings chatExecutionSettings = OpenAIPromptExecutionSettings.FromExecutionSettings(executionSettings);
|
||||
|
||||
parameters.Temperature = (float)chatExecutionSettings.Temperature;
|
||||
parameters.MaxTokens = chatExecutionSettings.MaxTokens ?? parameters.MaxTokens;
|
||||
|
||||
IList<KernelFunctionMetadata> functions = kernel?.Plugins.GetFunctionsMetadata().Where(x => x.PluginName == "AntSkFunctions").ToList() ?? [];
|
||||
var functionDefs = functions.Select(func => new FunctionDef(func.Name, func.Description, func.Parameters.Select(p => new FunctionParametersDef(p.Name, p.ParameterType?.IsClass == true ? "object" : "string", func.Description, p.IsRequired)).ToList())).ToList();
|
||||
|
||||
var messages = GetHistories(prompt);
|
||||
|
||||
return GetStreamingMessageAsync(messages, parameters, functionDefs, cancellationToken);
|
||||
|
||||
async IAsyncEnumerable<StreamingTextContent> GetStreamingMessageAsync(ChatMessage[] messages, ChatRequestParameters parameters, List<FunctionDef> functionDefs, CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (StreamedChatResponse msg in _client.ChatAsStreamAsync(_options.ModelVersion, messages, parameters, functionDefs.Count > 0 ? [.. functionDefs] : null, cancellationToken: cancellationToken))
|
||||
{
|
||||
if (msg.FunctionCall != null)
|
||||
{
|
||||
var func = functions.Where(x => x.Name == msg.FunctionCall.Name).FirstOrDefault();
|
||||
|
||||
if (kernel.Plugins.TryGetFunction(func.PluginName, func.Name, out var function))
|
||||
{
|
||||
var funcArgs = new Dictionary<string, object?>();
|
||||
|
||||
var JsonElement = JsonDocument.Parse(msg.FunctionCall.Arguments).RootElement;
|
||||
foreach (var parameter in func.Parameters)
|
||||
{
|
||||
if (JsonElement.TryGetProperty(parameter.Name, out var property))
|
||||
{
|
||||
funcArgs.Add(parameter.Name, property.Deserialize(parameter.ParameterType!, _jsonSerializerOptions));
|
||||
}
|
||||
}
|
||||
|
||||
var arguments = new KernelArguments
|
||||
{
|
||||
{ "args", funcArgs.Values.ToArray() }
|
||||
};
|
||||
|
||||
var result = (await function.InvokeAsync(kernel, arguments, cancellationToken)).GetValue<object>() ?? string.Empty;
|
||||
var stringResult = ProcessFunctionResult(result, chatExecutionSettings.ToolCallBehavior);
|
||||
messages = [.. messages, ChatMessage.FromUser($"""
|
||||
function call result:
|
||||
{stringResult}
|
||||
""")];
|
||||
|
||||
functionDefs.RemoveAll(x => x.Name == msg.FunctionCall.Name);
|
||||
|
||||
await foreach (var content in GetStreamingMessageAsync(messages, parameters, functionDefs, cancellationToken))
|
||||
{
|
||||
yield return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private ChatMessage[] GetHistories(string prompt)
|
||||
{
|
||||
var histories = prompt.Replace("history:", "")
|
||||
.Split("\r\n")
|
||||
.Select(m => m.Split(":", 2))
|
||||
.Where(m => m.Length == 2)
|
||||
.Select(pair => new ChatMessage(pair[0].Trim() == "user" ? "user" : "assistant", pair[1])).ToArray();
|
||||
|
||||
return histories;
|
||||
}
|
||||
|
||||
private static string? ProcessFunctionResult(object functionResult, ToolCallBehavior? toolCallBehavior)
|
||||
{
|
||||
if (functionResult is string stringResult)
|
||||
{
|
||||
return stringResult;
|
||||
}
|
||||
|
||||
if (functionResult is ChatMessageContent chatMessageContent)
|
||||
{
|
||||
return chatMessageContent.ToString();
|
||||
}
|
||||
|
||||
return JsonSerializer.Serialize(functionResult, _jsonSerializerOptions);
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ParseJsonElement(JsonElement element, string propertyName)
|
||||
{
|
||||
Dictionary<string, object> dict = new();
|
||||
|
||||
switch (element.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
foreach (JsonProperty property in element.EnumerateObject())
|
||||
{
|
||||
dict.Add(property.Name, ParseJsonElement(property.Value, property.Name));
|
||||
}
|
||||
break;
|
||||
|
||||
case JsonValueKind.Array:
|
||||
List<object> list = new List<object>();
|
||||
foreach (JsonElement arrayElement in element.EnumerateArray())
|
||||
{
|
||||
list.Add(ParseJsonElement(arrayElement, ""));
|
||||
}
|
||||
dict.Add(propertyName, list);
|
||||
break;
|
||||
|
||||
case JsonValueKind.String:
|
||||
dict.Add(propertyName, element.GetString());
|
||||
break;
|
||||
|
||||
case JsonValueKind.Number:
|
||||
dict.Add(propertyName, element.GetInt32());
|
||||
break;
|
||||
|
||||
case JsonValueKind.True:
|
||||
case JsonValueKind.False:
|
||||
dict.Add(propertyName, element.GetBoolean());
|
||||
break;
|
||||
|
||||
default:
|
||||
dict.Add(propertyName, "Unsupported value type");
|
||||
break;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user