Compare commits

...

78 Commits
0.1.7 ... 0.2.1

Author SHA1 Message Date
zyxucp
adbecb3b25 update 更新docker compose 2024-03-16 00:23:16 +08:00
zyxucp
277aacc34d add 动态添加dll 2024-03-15 23:53:53 +08:00
zyxucp
dba98f7968 add 增加上传dll功能 2024-03-15 22:55:23 +08:00
zyxucp
a2b0f3f3c2 add 插件导入 2024-03-15 22:45:36 +08:00
zyxucp
b5e527afdb add 增加函数列表 2024-03-15 22:33:39 +08:00
zyxucp
e84b05a39b fix 修改普通特性 2024-03-15 22:16:07 +08:00
zyxucp
631c563e71 fix 修改为普通特性,避免上传DLL需要引用特殊特性 2024-03-15 22:15:02 +08:00
zyxucp
47b304e46f update 升级SK到1.6.2 2024-03-15 22:08:22 +08:00
zyxucp
27ccfc5e88 add 增加分享使用 2024-03-15 21:57:21 +08:00
zyxucp
69441167d3 fix 修改高度自适应 2024-03-15 21:50:55 +08:00
zyxucp
4b97594217 fix 调整会话总结 2024-03-15 21:15:28 +08:00
zyxucp
9ee601f88c fix 首页调整 2024-03-15 21:12:31 +08:00
zyxucp
75b1a299e3 add 限制移动端屏幕缩放 2024-03-15 21:07:59 +08:00
zyxucp
2613c463a1 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-15 14:45:15 +08:00
zyxucp
78e0350d36 add 首页调整 2024-03-15 14:39:13 +08:00
zyxucp
bc2425fc3f Update README.md 2024-03-15 14:18:15 +08:00
zyxucp
3e861bc72f Update README.md 2024-03-15 13:01:35 +08:00
zyxucp
b41c464753 fix 暂时取消星火的会话拆分 2024-03-14 16:58:19 +08:00
zyxucp
2817275091 fix 暂时取消注册时间函数 2024-03-14 12:57:28 +08:00
zyxucp
e76b0cf326 fix 调整目录结构 2024-03-14 12:24:09 +08:00
zyxucp
cf34103e15 update docker-compose.yml 2024-03-14 12:07:17 +08:00
zyxucp
1588fd7d7a add 增加ErrorBoundary全局异常 2024-03-14 11:32:42 +08:00
zyxucp
4507ccde6c margin 2024-03-14 10:03:40 +08:00
zyxucp
73fffd766f fix 修复LLamaSharp给一个默认下载路径 2024-03-14 10:02:15 +08:00
zyxucp
e529146c5b fix 修改LLamaSharp文件夹给一个默认路径 2024-03-14 09:59:51 +08:00
zyxucp
7050e52009 Merge pull request #28 from ElderJames/main
Fix DI and OpenAI function calling
2024-03-13 22:25:45 +08:00
James Yeung
4f3238c4f6 Fix DI and OpenAI function calling 2024-03-13 22:22:22 +08:00
zyxucp
b7d27c5d50 fix 修复bug 2024-03-13 22:11:35 +08:00
zyxucp
fe94aa0564 fix 修复修改自身重复问题 2024-03-13 20:59:14 +08:00
zyxucp
ae60a9aced Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-13 12:13:56 +08:00
zyxucp
4f686b0871 fix 修复按钮停止不了的问题 2024-03-13 10:23:52 +08:00
zyxucp
c61840b7e8 Update README.md 2024-03-13 10:17:50 +08:00
zyxucp
9adce95367 fix appsettings.json示例 2024-03-13 10:10:52 +08:00
zyxucp
eef943458e fix 模型选择只能选gguf,修复搜索空指针页面 2024-03-13 10:02:00 +08:00
zyxucp
f5c80689d4 add 增加模型列表 自动下载模型 2024-03-13 00:52:46 +08:00
zyxucp
5eaee3130a add 增加模型下载页面 2024-03-13 00:10:30 +08:00
zyxucp
5846473f28 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-12 23:20:02 +08:00
zyxucp
94c019b484 Merge pull request #25 from ElderJames/model-download
support model download and file list
2024-03-12 22:51:15 +08:00
zyxucp
7e1140c022 add 增加下载页面 2024-03-12 22:50:06 +08:00
James Yeung
ea9044719a support model download and file list 2024-03-12 22:41:12 +08:00
zyxucp
8a96095448 add 增加模型下载地址 2024-03-12 21:57:34 +08:00
zyxucp
fcc8f8751b add 修改对外接口授权添加Bearer 2024-03-12 21:34:58 +08:00
zyxucp
af09ae7c3e Update docker-compose.simple.yml 2024-03-12 10:22:39 +08:00
zyxucp
e8e6a36d7b Update docker-compose.yml 2024-03-12 10:22:12 +08:00
zyxucp
4f89d54ef0 add mock类型便于测试 2024-03-12 09:57:12 +08:00
zyxucp
2f9e2fb114 fix 增加导入后清空文件列表 2024-03-12 09:33:55 +08:00
zyxucp
b6098024b8 add 更换markdown组件,实现代码高亮 2024-03-12 01:11:48 +08:00
zyxucp
1700131066 add 增加首页点击跳转 2024-03-12 00:31:17 +08:00
zyxucp
189536471a fix 先隐藏星火的KM选择器 2024-03-12 00:15:04 +08:00
zyxucp
f534e0bcc3 fix 修复多文件不能同时导入的bug 2024-03-12 00:12:41 +08:00
zyxucp
e203a18e92 add 增加原生插件调用示例 2024-03-11 23:42:26 +08:00
zyxucp
575a69bf4d add 增加首页,暂时取消本地函数调用 2024-03-11 23:16:23 +08:00
zyxucp
69fd3a0367 fix 修改命名空间 2024-03-11 21:23:07 +08:00
zyxucp
8f7e70298e fix 修改描述错误 2024-03-11 21:16:23 +08:00
zyxucp
0fa3f5a554 fix 移动文件目录 2024-03-11 19:41:51 +08:00
zyxucp
f420012752 fix 移动文件目录 2024-03-11 19:40:15 +08:00
zyxucp
c1ca916549 fix 增加openai模型示例 2024-03-11 19:38:26 +08:00
zyxucp
dfcf2bdc85 fix 取消非空类型 2024-03-11 19:12:47 +08:00
zyxucp
72e7acfb7d 修改写法 2024-03-11 19:09:41 +08:00
zyxucp
9d06c127dc fix 增加注释 2024-03-11 19:08:52 +08:00
zyxucp
0460b388ab fix 修改kernel 和km 每次build的问题,进行缓存 2024-03-11 19:02:17 +08:00
zyxucp
45b84ae898 fix 修改类名 2024-03-11 18:26:26 +08:00
zyxucp
41b1cb6f2d Merge pull request #24 from ElderJames/feat/sparkdesk-func-cal
Add support for the Function Calling of Spark Desk
2024-03-11 18:16:27 +08:00
James Yeung
159aaab38e Add support for the Function Calling of Spark Desk 2024-03-11 18:10:14 +08:00
zyxucp
dc351238f6 Update README.md 2024-03-11 15:13:23 +08:00
zyxucp
e6491b39c6 Update README.md 2024-03-11 15:11:11 +08:00
zyxucp
91b4ed8940 Update README.md 2024-03-11 14:23:16 +08:00
zyxucp
ab99098afd Update README.md 2024-03-11 13:26:41 +08:00
zyxucp
d14ce2faa0 add 增加系统设置管理权限 2024-03-10 21:26:01 +08:00
zyxucp
ca293691a8 fix 修改GpuLayerCount 默认值为10 2024-03-10 17:48:00 +08:00
zyxucp
cf8955b9b6 add 更新AntDesign KernelMemory nuget包版本 2024-03-10 16:31:16 +08:00
zyxucp
512828fdc9 fix 调整星火key和screct的位置 2024-03-10 10:55:44 +08:00
zyxucp
91299a96e7 Merge pull request #22 from ElderJames/main
Add Spark Desk text ceneration support
2024-03-10 09:21:35 +08:00
James Yeung
4876d9e727 fix build 2024-03-10 01:34:18 +08:00
James Yeung
a856f2a0e3 clean 2024-03-10 01:31:21 +08:00
James Yeung
0e8113e7b0 Add Spark Desk text ceneration support 2024-03-10 00:55:33 +08:00
zyxucp
34a953589d Update docker-compose.simple.yml 2024-03-09 23:55:50 +08:00
zyxucp
504ea5a238 Update docker-compose.yml 2024-03-09 23:55:40 +08:00
123 changed files with 3569 additions and 630 deletions

2
.gitignore vendored
View File

@@ -340,3 +340,5 @@ ASALocalRun/
/src/AntSK/AntSK.db
/src/AntSK/appsettings.Development.json
/src/AntSK.db
/src/AntSK/llama_models
/src/AntSK/AntSK.xml

View File

@@ -10,15 +10,19 @@
- **知识库**通过文档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类型的模型离线运行
- **国产信创**AntSK支持国产模型和国产数据库可以在信创条件下运行
## 应用场景
@@ -63,9 +67,9 @@ AntSK 适用于多种业务场景,例如:
在这里我使用的是Postgres 作为数据存储和向量存储因为Semantic Kernel和Kernel Memory都支持他当然你也可以换成其他的。
模型默认支持openai,如果需要使用azure openai需要调整SK的依赖注入可以使用one-api进行集成。
模型默认支持openaiazure openai 和llama支持的gguf本地模型,如果需要使用其他模型,可以使用one-api进行集成。
Login是默认的登陆账号和密码
配置文件中的Login配置是默认的登陆账号和密码
需要配置如下的配置文件
@@ -83,7 +87,7 @@ docker-compose up -d
```
来启动AntSK
## 如何在docker中挂载本地模型
## 如何在docker中挂载本地模型,和模型下载的目录
```
# 非 host 版本, 不使用本机代理
version: '3.8'
@@ -126,7 +130,8 @@ model/xxx.gguf
"LLamaSharp": {
"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": "D:\\Code\\AI\\AntBlazor\\model\\"
},
"Login": {
"User": "admin",
@@ -144,18 +149,19 @@ model/xxx.gguf
DBConnection.DbType
//连接字符串需要根据不同DB类型用对应的字符串
DBConnection.ConnectionStrings
//可以使用符合openai格式的在线API国产模型使用one-api转接 也可以使用AntSK自带的llama apiip和端口是AntSK启动地址
OpenAIOption.EndPoint
//模型秘钥如果使用本地模型可以默认NotNull 这里不能用中文
OpenAIOption.Key
//向量存储的类型,支持 Postgres Disk Memory 其中Postgres需要配置 ConnectionString
KernelMemory.VectorDb
//本地模型使用的运行方式 GUP CPU ,如果用在线API 这个随意使用一个即可
LLamaSharp.RunType
//本地会话模型的模型路径 注意区分linux和windows盘符不同
LLamaSharp.Chat
//本地向量模型的模型路径 注意区分linux和windows盘符不同
LLamaSharp.Embedding
//本地模型路径用于在选择llama时可以快速选择目录下的模型以及保存下载的模型
LLamaSharp.FileDirectory
//默认管理员账号密码
Login
//导入异步处理的线程数使用在线API可以高一点本地模型建议1 否则容易内存溢出崩掉

View File

@@ -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.2.1
ports:
- 5000:5000
networks:

View File

@@ -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.2.1
ports:
- 5000:5000
networks:

View File

@@ -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.36.2" />
<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.SemanticKernel" Version="1.6.2" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.6.2" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.6.2-alpha" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.34.240313.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.34.240313.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>

View File

@@ -48,6 +48,36 @@
<param name="value"></param>
<returns></returns>
</member>
<member name="T:AntSK.Domain.Domain.Model.Enum.AIType">
<summary>
AI类型
</summary>
</member>
<member name="T:AntSK.Domain.Domain.Model.Enum.AIModelType">
<summary>
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.MessageInfo.IsSend">
<summary>
发送是true 接收是false
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.PageIndex">
<summary>
当前页从1开始
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.PageSize">
<summary>
每页数量
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.TotalCount">
<summary>
总数
</summary>
</member>
<member name="F:AntSK.Domain.Domain.Other.LLamaConfig.dicLLamaWeights">
<summary>
避免模型重复加载,本地缓存
@@ -62,6 +92,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
@@ -92,61 +127,6 @@
<param name="history"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTOList``1(System.Object)">
<summary>
Entity集合转DTO集合
</summary>
<typeparam name="T"></typeparam>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTO``1(System.Object)">
<summary>
Entity转DTO
</summary>
<typeparam name="T"></typeparam>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.MapTo``1(System.Object,``0)">
<summary>
给已有对象map,适合update场景如需过滤空值需要在AutoMapProfile 设置
</summary>
<typeparam name="T"></typeparam>
<param name="self"></param>
<param name="result"></param>
<returns></returns>
</member>
<member name="T:AntSK.Domain.Model.Enum.AIType">
<summary>
AI类型
</summary>
</member>
<member name="T:AntSK.Domain.Model.Enum.AIModelType">
<summary>
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Model.MessageInfo.IsSend">
<summary>
发送是true 接收是false
</summary>
</member>
<member name="P:AntSK.Domain.Model.PageList`1.PageIndex">
<summary>
当前页从1开始
</summary>
</member>
<member name="P:AntSK.Domain.Model.PageList`1.PageSize">
<summary>
每页数量
</summary>
</member>
<member name="P:AntSK.Domain.Model.PageList`1.TotalCount">
<summary>
总数
</summary>
</member>
<member name="P:AntSK.Domain.Options.DBConnectionOption.DbType">
<summary>
sqlite连接字符串
@@ -252,6 +232,11 @@
插件列表
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.NativeFunctionList">
<summary>
本地函数列表
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.KmsIdList">
<summary>
知识库ID列表
@@ -262,6 +247,11 @@
API调用秘钥
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Funs.Path">
<summary>
接口描述
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.KmsDetails.FileName">
<summary>
文件名称
@@ -740,5 +730,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>

View File

@@ -0,0 +1,8 @@
namespace AntSK.Domain.Common
{
[AttributeUsage(AttributeTargets.Method)]
public class AntSkFunctionAttribute : Attribute
{
// 自定义的ActionAttribute
}
}

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Repositories;
using Microsoft.SemanticKernel;
using System;

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model;
namespace AntSK.Domain.Domain.Interface
{

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto;
using Microsoft.KernelMemory;
namespace AntSK.Domain.Domain.Interface

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Domain.Dto
namespace AntSK.Domain.Domain.Model.Dto
{
public class KMFile
{

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Domain.Dto
namespace AntSK.Domain.Domain.Model.Dto.OpenAPI
{
public class OpenAIModel
{

View File

@@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace AntSK.Domain.Domain.Dto
namespace AntSK.Domain.Domain.Model.Dto.OpenAPI
{
public class OpenAIResult
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Dto
namespace AntSK.Domain.Domain.Model.Dto
{
public class RelevantSource
{

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Model.Enum
namespace AntSK.Domain.Domain.Model.Enum
{
/// <summary>
/// AI类型
@@ -7,7 +7,9 @@
{
OpenAI = 1,
AzureOpenAI = 2,
LLamaSharp = 3
LLamaSharp = 3,
SparkDesk = 4,
Mock = 5,
}
/// <summary>

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Model.Enum
{
public enum AppType
{
chat = 1,
kms = 2
}
}

View File

@@ -1,9 +1,8 @@
namespace AntSK.Domain.Model
namespace AntSK.Domain.Domain.Model.Enum
{
public enum HttpMethodType
{
Get = 1,
Post = 2,
}
}
}

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Model.Enum
namespace AntSK.Domain.Domain.Model.Enum
{
public enum ImportKmsStatus
{

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Model.Fun
{
public class FunDto
{
public string Name { get; set; }
public string Description { get; set; }
}
}

View File

@@ -1,6 +1,6 @@
using AntSK.Domain.Repositories;
namespace AntSK.Domain.Model
namespace AntSK.Domain.Domain.Model
{
public class ImportKMSTaskDTO
{

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Model
namespace AntSK.Domain.Domain.Model
{
public class MessageInfo
{

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Model
namespace AntSK.Domain.Domain.Model
{
public class PageList<T>
{

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.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; }
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.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; }
}
}

View File

@@ -1,6 +1,6 @@
using AntSK.BackgroundTask;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model;
using Microsoft.Extensions.DependencyInjection;
namespace AntSK.Domain.Domain.Other

View File

@@ -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);

View File

@@ -10,16 +10,16 @@ using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Utils;
using Microsoft.KernelMemory;
using AntSK.Domain.Model;
using MarkdownSharp;
using AntSK.Domain.Domain.Dto;
using Markdig;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Dto;
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;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,128 @@
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Loader;
using System.Xml;
using AntSK.Domain.Common;
using AntSK.Domain.Utils;
using System.Text.RegularExpressions;
namespace AntSK.Domain.Domain.Service
{
public class FunctionService
{
private readonly Dictionary<string, MethodInfo> _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 Assembly[] _assemblies;
private readonly AssemblyLoadContext loadContext;
public FunctionService(IServiceProvider serviceProvider, Assembly[] assemblies)
{
_methodCache = [];
_methodInfos = [];
_serviceProvider = serviceProvider;
_assemblies = assemblies;
loadContext = new AssemblyLoadContext("AntSKLoadContext", true);
}
public Dictionary<string, MethodInfo> 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 =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description == "AntSK";
}));
}
}
//动态加载部分
var loadedAssemblies = loadContext.Assemblies.ToList();
foreach (var assembly in loadedAssemblies)
{
// 从缓存中获取标记了ActionAttribute的方法
foreach (var type in assembly.GetTypes())
{
markedMethods.AddRange(type.GetMethods().Where(m =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description == "AntSK";
}));
}
}
// 构建方法调用
foreach (var method in markedMethods)
{
var key = $"{method.DeclaringType.Assembly.GetName().Name}_{method.DeclaringType.Name}_{method.Name}";
string pattern = "[^a-zA-Z0-9_]";
// 使用 '-' 替换非ASCII的正则表达式的字符
key = Regex.Replace(key, pattern, "_");
_methodCache.TryAdd(key, method);
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);
if (string.IsNullOrEmpty(description))
{
description = "导入插件";
}
_methodInfos.TryAdd(key, (description, (method.ReflectedType, returnType), parameters));
}
}
public void FuncLoad(string pluginPath)
{
try
{
if (File.Exists(pluginPath))
{
string directory = Path.GetDirectoryName(pluginPath);
string fileName = Path.GetFileName(pluginPath);
var resolver = new AssemblyDependencyResolver(directory);
// Create a custom AssemblyLoadContext
loadContext.Resolving += (context, assemblyName) =>
{
string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return context.LoadFromAssemblyPath(assemblyPath);
}
return null;
};
// Load your assembly
Assembly pluginAssembly = loadContext.LoadFromAssemblyPath(pluginPath);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
}
}

View File

@@ -1,6 +1,6 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Repositories;
using Microsoft.KernelMemory;

View File

@@ -1,6 +1,6 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
@@ -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;
//}
}

View File

@@ -1,7 +1,7 @@
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;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using LLama;
@@ -11,7 +11,10 @@ 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;
using AntSK.Domain.Domain.Model.Enum;
namespace AntSK.Domain.Domain.Service
{
@@ -20,15 +23,22 @@ namespace AntSK.Domain.Domain.Service
{
private readonly IApis_Repositories _apis_Repositories;
private readonly IAIModels_Repositories _aIModels_Repositories;
private readonly FunctionService _functionService;
private readonly IServiceProvider _serviceProvider;
private Kernel _kernel;
public KernelService(
IApis_Repositories apis_Repositories,
IAIModels_Repositories aIModels_Repositories
)
IAIModels_Repositories aIModels_Repositories,
FunctionService functionService,
IServiceProvider serviceProvider)
{
_apis_Repositories = apis_Repositories;
_aIModels_Repositories = aIModels_Repositories;
_functionService = functionService;
_serviceProvider = serviceProvider;
}
/// <summary>
/// 获取kernel实例依赖注入不好按每个用户去Import不同的插件所以每次new一个新的kernel
/// </summary>
@@ -37,20 +47,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 +76,7 @@ namespace AntSK.Domain.Domain.Service
apiKey: chatModel.ModelKey,
httpClient: chatHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
builder.AddAzureOpenAIChatCompletion(
deploymentName: chatModel.ModelName,
@@ -67,11 +84,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,25 +108,33 @@ 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
{
Console.WriteLine(msg);
RestClient client = new RestClient();
RestRequest request = new RestRequest(api.Url, Method.Get);
foreach (var header in api.Header.Split("\n"))
foreach (var header in api.Header.ConvertToString().Split("\n"))
{
var headerArray = header.Split(":");
if (headerArray.Length == 2)
@@ -109,7 +143,7 @@ namespace AntSK.Domain.Domain.Service
}
}
//这里应该还要处理一次参数提取,等后面再迭代
foreach (var query in api.Query.Split("\n"))
foreach (var query in api.Query.ConvertToString().Split("\n"))
{
var queryArray = query.Split("=");
if (queryArray.Length == 2)
@@ -126,15 +160,16 @@ 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
{
Console.WriteLine(msg);
RestClient client = new RestClient();
RestRequest request = new RestRequest(api.Url, Method.Post);
foreach (var header in api.Header.Split("\n"))
foreach (var header in api.Header.ConvertToString().Split("\n"))
{
var headerArray = header.Split(":");
if (headerArray.Length == 2)
@@ -143,7 +178,7 @@ namespace AntSK.Domain.Domain.Service
}
}
//这里应该还要处理一次参数提取,等后面再迭代
request.AddJsonBody(api.JsonBody);
request.AddJsonBody(api.JsonBody.ConvertToString());
var result = client.Execute(request);
return result.Content;
}
@@ -155,19 +190,39 @@ namespace AntSK.Domain.Domain.Service
break;
}
}
_kernel.ImportPluginFromFunctions("ApiFunctions", functions);
}
//本地函数插件
if (!string.IsNullOrWhiteSpace(app.NativeFunctionList))//需要添加判断应用是否开启了本地函数插件
{
var nativeIdList = app.NativeFunctionList.Split(",");
_functionService.SearchMarkedMethods();
using var scope= _serviceProvider.CreateScope();
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 };
var target = ActivatorUtilities.CreateInstance(scope.ServiceProvider, func.Value.DeclaringType);
apiFunctions.Add(_kernel.CreateFunctionFromMethod(func.Value, target, 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");
//kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
kernel.ImportPluginFromPromptDirectory(Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
}
@@ -183,8 +238,8 @@ namespace AntSK.Domain.Domain.Service
KernelFunction sunFun = _kernel.Plugins.GetFunction("ConversationSummaryPlugin", "SummarizeConversation");
var summary = await _kernel.InvokeAsync(sunFun, new() { ["input"] = $"内容是:{history.ToString()} {Environment.NewLine} 请注意用中文总结" });
string his = summary.GetValue<string>();
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}"; ;
var msg = $"history{Environment.NewLine}{history.ToString()}{Environment.NewLine} user{questions}{Environment.NewLine}"; ;
return msg;
}
}
}
}

View File

@@ -1,13 +0,0 @@
using AutoMapper;
namespace AntSK.Domain.Map
{
public class AutoMapProfile : Profile
{
public AutoMapProfile()
{
}
}
}

View File

@@ -1,48 +0,0 @@
using AutoMapper;
namespace AntSK.Domain.Map
{
public static class MapperExtend
{
/// <summary>
/// Entity集合转DTO集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static List<T> ToDTOList<T>(this object value)
{
if (value == null)
return new List<T>();
return Mapper.Map<List<T>>(value);
}
/// <summary>
/// Entity转DTO
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static T ToDTO<T>(this object value)
{
if (value == null)
return default(T);
return Mapper.Map<T>(value);
}
/// <summary>
/// 给已有对象map,适合update场景如需过滤空值需要在AutoMapProfile 设置
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="self"></param>
/// <param name="result"></param>
/// <returns></returns>
public static T MapTo<T>(this object self, T result)
{
if (self == null)
return default(T);
return (T)Mapper.Map(self, result, self.GetType(), typeof(T));
}
}
}

View File

@@ -1,30 +0,0 @@
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
namespace AntSK.Domain.Map
{
public static class MapperRegister
{
public static void AddMapper(this IServiceCollection services)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
cfg.ShouldMapMethod = m => false;
cfg.AddProfile<AutoMapProfile>();
});
IMapper mapper = config.CreateMapper();
//启动实体映射
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
cfg.ShouldMapMethod = m => false;
cfg.AddProfile<AutoMapProfile>();
});
}
}
}

View File

@@ -6,5 +6,7 @@
public static string Chat { get; set; }
public static string Embedding { get; set; }
public static string FileDirectory { get; set; } = Directory.GetCurrentDirectory();
}
}

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model.Enum;
using SqlSugar;
using System.ComponentModel.DataAnnotations;

View File

@@ -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列表

View File

@@ -0,0 +1,20 @@
using AntSK.Domain.Domain.Model.Enum;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace AntSK.Domain.Repositories
{
[SugarTable("Funs")]
public partial class Funs
{
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// 接口描述
/// </summary>
[Required]
public string Path { get; set; }
}
}

View File

@@ -0,0 +1,11 @@

using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Repositories.Base;
namespace AntSK.Domain.Repositories
{
[ServiceDescription(typeof(IFuns_Repositories), ServiceLifetime.Scoped)]
public class Funs_Repositories : Repository<Funs>, IFuns_Repositories
{
}
}

View File

@@ -0,0 +1,8 @@
using AntSK.Domain.Repositories.Base;
namespace AntSK.Domain.Repositories
{
public interface IFuns_Repositories : IRepository<Funs>
{
}
}

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model.Enum;
using SqlSugar;
namespace AntSK.Domain.Repositories

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model;
using SqlSugar;
using System.Linq.Expressions;

View File

@@ -1,5 +1,6 @@
using AntSK.Domain.Map;
using AntSK.Domain.Model;
using AntSK.Domain.Common.Map;
using AntSK.Domain.Domain.Model;
using SqlSugar;
using System.Linq.Expressions;

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model.Enum;
using SqlSugar;
using System.ComponentModel.DataAnnotations;

View 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 &lt and &gt 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);
}
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

24
src/AntSK.Test/Class1.cs Normal file
View File

@@ -0,0 +1,24 @@
using System.ComponentModel;
namespace AntSK.Test
{
/// <summary>
/// 测试插件导入
/// </summary>
public class TestFunctionImport
{
/// <summary>
/// 获取名称
/// </summary>
/// <returns>返回名称</returns>
[Description("AntSK")]
public string GetName()
{
return $"""
我的名字是AntSK,
我的作者是许泽宇
我是一个AI 知识库/智能体项目
""";
}
}
}

View File

@@ -18,6 +18,10 @@ 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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntSK.Test", "AntSK.Test\AntSK.Test.csproj", "{6AD71410-127F-4C83-95A8-F699C39B44FF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,6 +40,14 @@ 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
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -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>

View File

@@ -1,148 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>AntSK</name>
</assembly>
<members>
<member name="M:AntSK.Controllers.FileController.UploadFile(Microsoft.AspNetCore.Http.IFormFile)">
<summary>
Upload FileName
</summary>
<returns></returns>
</member>
<member name="M:AntSK.Controllers.InitController.InitTable">
<summary>
初始化DB 和表
</summary>
<returns></returns>
</member>
<member name="T:AntSK.Controllers.KMSController">
<summary>
</summary>
<param name="_taskBroker"></param>
</member>
<member name="M:AntSK.Controllers.LLamaSharpController.chat(AntSK.Domain.Domain.Dto.OpenAIModel)">
<summary>
本地会话接口
</summary>
<returns></returns>
</member>
<member name="M:AntSK.Controllers.LLamaSharpController.embedding(AntSK.Domain.Domain.Dto.OpenAIEmbeddingModel)">
<summary>
本地嵌入接口
</summary>
<param name="model"></param>
<returns></returns>
</member>
<member name="T:AntSK.Controllers.OpenController">
<summary>
对外接口
</summary>
</member>
<member name="M:AntSK.Controllers.OpenController.#ctor(AntSK.Services.OpenApi.IOpenApiService)">
<summary>
对外接口
</summary>
</member>
<member name="M:AntSK.Controllers.OpenController.chat(AntSK.Domain.Domain.Dto.OpenAIModel)">
<summary>
对话接口
</summary>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.Chat.SendKms(System.String,System.String,AntSK.Domain.Repositories.Apps)">
<summary>
发送知识库问答
</summary>
<param name="questions"></param>
<param name="msg"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.Chat.SendChat(System.String,System.String,AntSK.Domain.Repositories.Apps)">
<summary>
发送普通对话
</summary>
<param name="questions"></param>
<param name="history"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.Chat.HistorySummarize(AntSK.Domain.Repositories.Apps,System.String)">
<summary>
历史会话的会话总结
</summary>
<param name="questions"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.OpenChat.SendKms(System.String,System.String,AntSK.Domain.Repositories.Apps)">
<summary>
发送知识库问答
</summary>
<param name="questions"></param>
<param name="msg"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.OpenChat.SendChat(System.String,System.String,AntSK.Domain.Repositories.Apps)">
<summary>
发送普通对话
</summary>
<param name="questions"></param>
<param name="history"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.OpenChat.HistorySummarize(AntSK.Domain.Repositories.Apps,System.String)">
<summary>
历史会话的会话总结
</summary>
<param name="questions"></param>
<returns></returns>
</member>
<member name="T:AntSK.Pages.KmsPage.KmsDetail.UrlModel">
<summary>
根据文档ID获取文档
</summary>
<param name="fileid"></param>
<returns></returns>
</member>
<member name="T:AntSK.Services.LLamaSharp.LLamaChatService">
<summary>
</summary>
</member>
<member name="T:AntSK.Services.LLamaSharp.LLamaEmbeddingService">
<summary>
本地Embedding
</summary>
</member>
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendChat(System.String,AntSK.Domain.Repositories.Apps)">
<summary>
发送普通对话
</summary>
<param name="questions"></param>
<param name="msg"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendKms(System.String,AntSK.Domain.Repositories.Apps)">
<summary>
发送知识库问答
</summary>
<param name="questions"></param>
<param name="msg"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Services.OpenApi.OpenApiService.HistorySummarize(AntSK.Domain.Repositories.Apps,AntSK.Domain.Domain.Dto.OpenAIModel)">
<summary>
历史会话的会话总结
</summary>
<param name="questions"></param>
<param name="msg"></param>
<returns></returns>
</member>
</members>
</doc>

View File

@@ -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 />

View 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>

View 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; }
}
}

View 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;
}

View File

@@ -3,7 +3,8 @@
@inherits AntDomComponentBase
<Space Class="@ClassMapper.Class" Size="@("26")">
<SpaceItem>
<Image Src="http://img.shields.io/github/stars/aidotnet/antsk?style=social" Width="88px" Height="20px;"></Image>
<SpaceItem Style="margin-left:20px;">
<AvatarDropdown Name="@context.Identity.Name"
Avatar="@_currentUser.Avatar"
MenuItems="@AvatarMenuItems"

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using AntSK.Domain.Options;
using Microsoft.AspNetCore.Mvc;
namespace AntSK.Controllers
{
@@ -20,7 +21,7 @@ namespace AntSK.Controllers
}
// 创建文件存储的路径
var uploadsFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "files"); // 给定的文件夹名称
var uploadsFolderPath = Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory), "files");// 给定的文件夹名称
// 如果路径不存在,则创建一个新的目录
if (!Directory.Exists(uploadsFolderPath))

View File

@@ -1,8 +1,8 @@
using AntSK.BackgroundTask;
using AntSK.Domain.Common.Map;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Map;
using AntSK.Domain.Model;
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Repositories;
using Microsoft.AspNetCore.Mvc;

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Services.LLamaSharp;
using Microsoft.AspNetCore.Mvc;

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Domain.Utils;
using AntSK.Services.OpenApi;
using Microsoft.AspNetCore.Mvc;

View File

@@ -5,16 +5,25 @@
@using AntSK.Domain.Options
@using AntSK.Domain.Repositories
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject INotificationService _notice
@inherits LayoutComponentBase
<AntDesign.ProLayout.BasicLayout
Logo="@("https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg")"
MenuData="_menuData">
<AntDesign.ProLayout.BasicLayout Logo="@("https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg")"
MenuData="_menuData">
<RightContentRender>
<AntSK.Components.RightContent />
</RightContentRender>
<ChildContent>
@Body
<ErrorBoundary @ref="errorBoundary">
<ChildContent>
@Body
</ChildContent>
<ErrorContent Context="ex">
@{
ShowNotification(ex);
}
</ErrorContent>
</ErrorBoundary>
</ChildContent>
<FooterRender>
<FooterView Copyright="2024 许泽宇的技术分享" Links="Links"></FooterView>
@@ -24,6 +33,7 @@
@code
{
ErrorBoundary errorBoundary;
private MenuDataItem[] _menuData = { };
[Inject] public HttpClient HttpClient { get; set; }
@@ -36,7 +46,7 @@
{
await base.OnInitializedAsync();
//菜单权限控制
var menuList = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
var menuList = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
var userSessionStorageResult = await _protectedSessionStore.GetAsync<UserSession>("UserSession");
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
@@ -54,7 +64,7 @@
}
if (username == LoginOption.User )
if (username == LoginOption.User)
{
_menuData = menuList;
}
@@ -68,6 +78,17 @@
}
void ShowNotification(Exception ex)
{
_ = _notice.Error(new()
{
Message = ex.Message,
Description = ex.StackTrace
});
errorBoundary.Recover();
}
public LinkItem[] Links { get; set; } =
{
new LinkItem
@@ -81,9 +102,9 @@
{
Key = "github",
Title = (RenderFragment)(@<Icon Type="github" />),
Href = "https://github.com/xuzeyu91/Xzy.AntSK.KnowledgeBase",
Href = "https://github.com/AIDotNet/AntSK",
BlankTarget = true,
}
};
}

View File

@@ -1,6 +1,7 @@
@namespace AntSK
@inherits LayoutComponentBase
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
@Body
@code {

View File

@@ -36,7 +36,7 @@
{
Key = "github",
Title = (RenderFragment)(@<Icon Type="github" />),
Href = "https://github.com/xuzeyu91/Xzy.AntSK.KnowledgeBase",
Href = "https://github.com/AIDotNet/AntSK",
BlankTarget = true,
}
};

View File

@@ -0,0 +1,8 @@
namespace AntSK.Models
{
public class FileInfoModel
{
public string FileName { get; set; }
public string FilePath { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
@namespace AntSK.Pages.AppPage
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.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"

View File

@@ -1,7 +1,10 @@
using AntDesign;
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.SemanticKernel;
namespace AntSK.Pages.AppPage
{
@@ -24,6 +27,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 +36,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 +53,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 +67,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 +85,16 @@ namespace AntSK.Pages.AppPage
}
_appModel.KmsIdList = string.Join(",", kmsIds);
}
if (apiIds != null && apiIds.Count() > 0)
if (apiIds.IsNotNull())
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
if (funIds.IsNotNull())
{
_appModel.NativeFunctionList = string.Join(",", funIds);
}
if (string.IsNullOrEmpty(AppId))
{
@@ -103,6 +126,11 @@ namespace AntSK.Pages.AppPage
{
NavigationManager.NavigateTo("/applist");
}
private void NavigateModelList()
{
NavigationManager.NavigateTo("/setting/modellist");
}
}
}

View File

@@ -1,5 +1,6 @@
@namespace AntSK.Pages.AppPage
@using AntSK.Domain.Repositories
@using AntSK.Domain.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>

View File

@@ -1,5 +1,5 @@
using AntDesign;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Domain.Repositories;
using AntSK.Models;
using Microsoft.AspNetCore.Components;

View File

@@ -10,7 +10,7 @@
<GridRow Gutter="(16, 16)">
<GridCol Span="12">
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
<Card Style="height:700px;overflow: auto;">
<Card Style="height:75vh;overflow: auto;">
<TitleTemplate>
<Icon Type="setting" /> 选择应用
<Select DataSource="@_list"
@@ -20,9 +20,10 @@
LabelProperty="c=>c.Name"
Style="width:200px">
</Select>
<a href="@( NavigationManager.BaseUri + "openchat/" + AppId)" target="_blank">分享使用</a>
</TitleTemplate>
<Body>
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
<div id="scrollDiv" style="height: calc(75vh - 170px); overflow-y: auto; overflow-x: hidden;">
<GridRow Gutter="(8, 8)" Style="margin:0">
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
@if (item.IsSend)
@@ -69,7 +70,7 @@
</Spin>
</GridCol>
<GridCol Span="12">
<Card Style="height: 700px;overflow: auto;">
<Card Style="height: 75vh;overflow: auto;">
<TitleTemplate>
<Icon Type="search" /> 调试结果
</TitleTemplate>
@@ -95,7 +96,6 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 10px;
justify-content: center;
align-items: flex-start;
height: 100vh;

View File

@@ -1,10 +1,7 @@
using AntDesign;
using AntSK.Domain.Domain.Dto;
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 +10,9 @@ using Microsoft.SemanticKernel.Connectors.OpenAI;
using RestSharp;
using System.Text;
using AntSK.Domain.Utils;
using Markdig;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Dto;
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>
@@ -259,7 +262,7 @@ namespace AntSK.Pages.ChatPage
}
else
{
var msg = $"history{history.ToString()}{Environment.NewLine}";
var msg = $"history{Environment.NewLine}{history.ToString()}{Environment.NewLine}{Environment.NewLine}";
return msg;
}
}

View File

@@ -1,9 +1,7 @@
using AntDesign;
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 +10,8 @@ using SqlSugar;
using System.Text;
using AntSK.Domain.Utils;
using Microsoft.JSInterop;
using Markdig;
using AntSK.Domain.Domain.Model;
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>

View File

@@ -0,0 +1,76 @@
using AntSK.Domain.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");
}
}
}

View File

@@ -1,41 +1,166 @@
@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>
<Divider><Title Level="3">功能特性</Title></Divider>
<Space Size=@(("10", "10" )) Wrap Align="start" Style="padding:10px">
<SpaceItem>
<Card Bordered="true" Title=@("🍬语义内核") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Text>1、采用遥遥领先的自然语言处理技术</Text>
<br>
<Text>2、准确理解、处理和响应复杂的语义查询</Text>
<br>
<Text>3、为用户提供精确的信息检索和推荐服务</Text>
<br>
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("🏁知识库") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Text>1、通过文档Word、PDF、Excel、Txt、Markdown、Json、PPT等形式导入知识库</Text>
<br>
<Text>2、可以进行知识库文档搜索问答</Text>
<br>
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("🌈GPTs 生成") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Text>1、支持创建个性化的GPTs</Text>
<br>
<Text>2、构建您自己的GPTs</Text>
<br>
<Text>3、可以尽情发挥你的想象力</Text>
<br>
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("🎁插件生态") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Text>1、开放式API插件系统允许第三方开发者或服务商轻松将其接口集成到AntSK不断增强应用功能</Text>
<br>
<Text>2、开放式函数插件系统标准格式的函数代码以及dll后集成到AntSK不断增强应用功能</Text>
<br>
<Text>3、支持二次开发Function</Text>
<br>
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("📱模型管理") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Text>1、适配和管理集成不同厂商的不同模型</Text>
<br>
<Text>
2、并且支持llama.cpp所支持的gguf类型的模型离线运行
</Text>
<br>
<Text>
3、未来将实现模型的训练、微调、部署一站式服务
</Text>
<br>
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("🔗国产信创") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Text>1、支持国产本地化模型</Text>
<br>
<Text>
2、支持国产信创数据库
</Text>
<br
<Text>
3、支持信创环境运行
</Text>
<br>
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("🥤如果本项目帮助到了您") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Image Height="170" Src="./assets/zfb.png" />
</Body>
</Card>
</SpaceItem>
<SpaceItem>
<Card Bordered="true" Title=@("您可用以下方式支持~🥤") Hoverable="true" Style="height:260px;width:300px">
<Body>
<Image Height="170" Src="./assets/wx.png" />
</Body>
</Card>
</SpaceItem>
</Space>
</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**项目进展。");
}

View 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;
}
}

View File

@@ -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>

View File

@@ -1,5 +1,5 @@
using AntDesign;
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Repositories;
using Microsoft.AspNetCore.Components;
@@ -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");
}
}
}

View File

@@ -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">

View File

@@ -1,13 +1,16 @@
using AntDesign;
using AntSK.BackgroundTask;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.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
});
}
}

View File

@@ -1,6 +1,6 @@
@namespace AntSK.Pages.KmsPage
@using AntSK.Domain.Repositories
@using AntSK.Domain.Domain.Dto
@using AntSK.Domain.Domain.Model.Dto
@page "/Kms/DetailList/{KmsID}/{FileId}"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth

View File

@@ -1,5 +1,5 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Dto;
using Microsoft.AspNetCore.Components;
namespace AntSK.Pages.KmsPage

View File

@@ -1,7 +1,7 @@
@namespace AntSK.Pages.ApiPage
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model
@using AntSK.Domain.Domain.Model.Enum
@page "/plugins/api/add"
@page "/plugins/api/add/{ApiId}"
@using AntSK.Services.Auth
@@ -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)

View File

@@ -1,6 +1,6 @@
@namespace AntSK.Pages.ApiPage
@using AntSK.Domain.Repositories
@using AntSK.Domain.Model
@using AntSK.Domain.Domain.Model.Enum
@page "/plugins/apilist"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth

View File

@@ -0,0 +1,83 @@
@namespace AntSK.Pages.ApiPage
@using AntSK.Domain.Repositories
@using AntSK.Domain.Domain.Model.Enum
@using AntSK.Domain.Domain.Model.Fun
@page "/plugins/funlist"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="函数列表">
<Content>
<div style="text-align: center;">
<Search Placeholder="输入回车"
EnterButton="@("搜索")"
Size="large"
Style="max-width: 522px; width: 100%;"
OnSearch="Search" />
</div>
</Content>
<ChildContent>
<div class="cardList">
<AntList TItem="FunDto"
DataSource="_data"
ItemLayout="ListItemLayout.Horizontal"
Grid="LayoutModel._listGridType">
<ListItem NoFlex>
@if (string.IsNullOrEmpty(context.Name))
{
<Button Type="dashed" class="newButton" @onclick="AddFun">
<Icon Type="plus" Theme="outline" /> 创建函数
</Button>
}
else
{
<Card Hoverable Bordered Class="card" Style="max-height:247px;">
<CardMeta>
<AvatarTemplate>
</AvatarTemplate>
<TitleTemplate>
<a>@context.Name</a>
</TitleTemplate>
<DescriptionTemplate>
<Paragraph class="item" Ellipsis>
<!--todo: Ellipsis not working-->
@context.Description
</Paragraph>
</DescriptionTemplate>
</CardMeta>
</Card>
}
</ListItem>
</AntList>
</div>
</ChildContent>
</PageContainer>
<Modal Title="插件导入"
Visible="@_fileVisible"
OnOk="@FileHandleOk"
OnCancel="@FileHandleCancel"
ConfirmLoading="@_fileConfirmLoading">
<Upload Action="@("api/File/UploadFile")"
Name="file"
Drag
Multiple
Accept="*/*"
BeforeUpload="BeforeUpload"
OnSingleCompleted="OnSingleCompleted">
<p class="ant-upload-drag-icon">
<Icon Type="inbox" />
</p>
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
<p class="ant-upload-hint">
请上传dll文件
</p>
</Upload>
</Modal>
@code
{
}

View File

@@ -0,0 +1,130 @@
using AntDesign;
using AntSK.Domain.Domain.Model.Fun;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Repositories;
using AntSK.Models;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.SemanticKernel;
namespace AntSK.Pages.ApiPage
{
public partial class FunList
{
private FunDto[] _data = { };
[Inject]
FunctionService _functionService { get; set; }
[Inject]
IServiceProvider _serviceProvider { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
[Inject]
IFuns_Repositories _funs_Repositories { get; set; }
[Inject]
protected MessageService? _message { get; set; }
bool _fileVisible = false;
bool _fileConfirmLoading = false;
List<FileInfoModel> fileList = new List<FileInfoModel>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await InitData("");
}
private async Task InitData(string searchKey)
{
var list = new List<FunDto> { new FunDto() };
_functionService.SearchMarkedMethods();
using var scope = _serviceProvider.CreateScope();
var funList = _functionService.Functions;
if (!string.IsNullOrEmpty(searchKey))
{
funList = funList.Where(x => x.Key.Contains(searchKey)).ToDictionary(x => x.Key, x => x.Value);
}
foreach (var func in funList)
{
var methodInfo = _functionService.MethodInfos[func.Key];
list.Add(new FunDto() { Name = func.Key, Description = methodInfo.Description });
}
_data = list.ToArray();
await InvokeAsync(StateHasChanged);
}
private void NavigateToAddApp()
{
NavigationManager.NavigateTo("/plugins/fun/add");
}
private async Task Search(string searchKey)
{
await InitData(searchKey);
}
private async Task AddFun() {
_fileVisible = true;
}
private async Task FileHandleOk(MouseEventArgs e)
{
try
{
foreach (var file in fileList)
{
_funs_Repositories.Insert(new Funs() { Id = Guid.NewGuid().ToString(), Path = file.FilePath });
_functionService.FuncLoad(file.FilePath);
}
_message.Info("上传成功");
await InitData("");
_fileVisible = false;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void FileHandleCancel(MouseEventArgs e)
{
_fileVisible = false;
}
private void FileShowModal()
{
_fileVisible = true;
}
bool BeforeUpload(UploadFileItem file)
{
if (file.Ext != ".dll")
{
_message.Error("请上传dll文件!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return IsLt500K;
}
private void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
{
//文件列表
fileList.Add(new FileInfoModel()
{
FileName = fileinfo.File.FileName,
FilePath = fileinfo.File.Url = fileinfo.File.Response
});
}
}
}
}

View File

@@ -0,0 +1,132 @@
/* 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 */
.cardList .card .ant-card-meta-title {
margin-bottom: 12px;
}
.cardList .card .ant-card-meta-title > a {
display: inline-block;
max-width: 100%;
color: rgba(0, 0, 0, 0.85);
}
.cardList .card .ant-card-body:hover .ant-card-meta-title > a {
color: #1890ff;
}
.cardList .item {
height: 64px;
}
.cardList .ant-list .ant-list-item-content-single {
max-width: 100%;
}
.extraImg {
width: 155px;
margin-top: -20px;
text-align: center;
}
.extraImg img {
width: 100%;
}
.newButton {
width: 100%;
height: 201px;
color: rgba(0, 0, 0, 0.45);
background-color: #fff;
border-color: #d9d9d9;
}
.cardAvatar {
width: 48px;
height: 48px;
border-radius: 48px;
}
.cardDescription {
position: relative;
max-height: 4.5em;
margin-right: -1em;
padding-right: 1em;
overflow: hidden;
line-height: 1.5em;
text-align: justify;
}
.cardDescription::before {
position: absolute;
right: 14px;
bottom: 0;
padding: 0 1px;
background: #fff;
content: '...';
}
.cardDescription::after {
position: absolute;
right: 14px;
width: 1em;
height: 1em;
margin-top: 0.2em;
background: white;
content: '';
}
.pageHeaderContent__b__1 {
position: relative;
}
.contentLink {
margin-top: 16px;
}
.contentLink a {
margin-right: 32px;
}
.contentLink a img {
width: 24px;
}
.contentLink img {
margin-right: 8px;
vertical-align: middle;
}
@media screen and (max-width: 992px) {
.contentLink a {
margin-right: 16px;
}
}
@media screen and (max-width: 768px) {
.extraImg {
display: none;
}
}
@media screen and (max-width: 576px) {
.pageHeaderContent__b__1 {
padding-bottom: 30px;
}
.contentLink {
position: absolute;
bottom: -4px;
left: 0;
width: 1000px;
}
.contentLink a {
margin-right: 16px;
}
.contentLink img {
margin-right: 4px;
}
}

View File

@@ -1,12 +1,14 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model.Enum
@using AntSK.Domain.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>

View File

@@ -1,8 +1,12 @@
using AntDesign;
using AntDesign.ProLayout;
using AntSK.Domain.Domain.Model.Enum;
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,27 +14,58 @@ 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 = AIType.LLamaSharp;
_downloadModalVisible = true;
_downloadUrl = $"https://hf-mirror.com{ModelPath.Replace("---","/")}";
}
}
catch
{
_ = Message.Error("LLamaSharp.FileDirectory目录配置不正确", 2);
}
}
private void HandleSubmit()
{
if (_aimodels_Repositories.IsAny(p => p.AIModelType == _aiModel.AIModelType && p.EndPoint == _aiModel.EndPoint.ConvertToString() && p.ModelKey == _aiModel.ModelKey && p.ModelName == _aiModel.ModelName))
if (_aimodels_Repositories.IsAny(p => p.Id!=_aiModel.Id.ConvertToString()&& p.AIModelType == _aiModel.AIModelType && p.EndPoint == _aiModel.EndPoint.ConvertToString() && p.ModelKey == _aiModel.ModelKey && p.ModelName == _aiModel.ModelName))
{
_ = Message.Error("模型已存在!", 2);
return;
@@ -69,5 +104,69 @@ 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();
InvokeAsync(StateHasChanged);
}
}
}

View File

@@ -1,15 +1,63 @@
@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.Domain.Model.hfmirror;
using AntSK.Domain.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>;
}

View File

@@ -0,0 +1,52 @@
using AntDesign;
using AntSK.Models;
using AntSK.Services;
using DocumentFormat.OpenXml.Office2010.Excel;
using Microsoft.AspNetCore.Components;
using Newtonsoft.Json;
using RestSharp;
using AntSK.Domain.Utils;
using AntSK.Domain.Domain.Model.hfmirror;
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}");
}
}
}

View 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;
}

View File

@@ -0,0 +1,55 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/modeldown/detail/{ModelName}/{ModelPath}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@using AntSK.Domain.Domain.Model.hfmirror;
using AntSK.Domain.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>;
}

View File

@@ -0,0 +1,56 @@
using AntSK.Domain.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("/", "---")}");
}
}
}

View 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;
}

View File

@@ -1,10 +1,12 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Domain.Model.Enum
@using AntSK.Domain.Domain.Model.Enum
@page "/setting/modellist"
@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>

View File

@@ -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>

View File

@@ -5,6 +5,8 @@
@inject IMessageService _message
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "AntSKAdmin")]
<PageContainer Title="个人设置">

View File

@@ -4,6 +4,8 @@
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "AntSKAdmin")]
<div>
<PageContainer Title="用户管理">

View File

@@ -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>

Some files were not shown because too many files have changed in this diff Show More