Compare commits

...

131 Commits
0.1.0 ... 0.1.4

Author SHA1 Message Date
zyxucp
d4c804c4ac fix 删除不要注释 2024-03-06 12:32:10 +08:00
zyxucp
c3003f8c3a add 增加注释 2024-03-06 12:24:31 +08:00
zyxucp
66a2e45029 fix 修改知识库提示词结构 2024-03-06 12:23:31 +08:00
zyxucp
b0c889c336 fix 优化知识库提示词 2024-03-06 12:03:33 +08:00
zyxucp
f470b1e555 fix 修改bug 2024-03-06 11:48:21 +08:00
zyxucp
4965983ae1 add 修改知识库为流式输出 2024-03-06 11:25:20 +08:00
zyxucp
2f2b4e2f3c Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-06 10:14:38 +08:00
zyxucp
16c739e37f fix 增加异常处理 2024-03-06 10:05:34 +08:00
zyxucp
f4d51a73cd fix 描述修改 2024-03-06 10:03:40 +08:00
zyxucp
6cc29e4167 fix 处理提示词不包含input的情况 2024-03-05 20:46:20 +08:00
zyxucp
324e1de84f Update README.md 2024-03-05 18:31:11 +08:00
zyxucp
551bdf4802 add 增加知识库切片配置 2024-03-05 14:33:00 +08:00
zyxucp
761d9069f6 add 增加切片设置 2024-03-05 13:41:26 +08:00
zyxucp
7a15b0f444 fix 增加key注释 2024-03-05 09:46:47 +08:00
zyxucp
a185ee4841 add 增加文本格式导入 2024-03-04 22:30:51 +08:00
zyxucp
7213a75212 fix 修改样式 2024-03-04 22:12:03 +08:00
zyxucp
4823977098 add 增加模型下载页 2024-03-04 22:01:37 +08:00
zyxucp
fa74b84ce3 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-04 21:44:36 +08:00
zyxucp
7423b23d3c fix 修改总结判定 2024-03-04 21:38:22 +08:00
zyxucp
033ded08c7 fix 修改总结判定条件 2024-03-04 21:36:46 +08:00
zyxucp
6b397c86d2 fix 修改总结判定条件 2024-03-04 21:34:51 +08:00
zyxucp
9730f12c56 Update README.md 2024-03-04 21:10:46 +08:00
zyxucp
92c7a80c2e add 增加docker-compose 2024-03-04 20:46:42 +08:00
zyxucp
64029de11a fix 修改docker file 2024-03-04 17:41:31 +08:00
zyxucp
ae363ebad6 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-04 09:58:21 +08:00
zyxucp
7b6478b7f5 fix 修改模型名 2024-03-04 09:57:41 +08:00
zyxucp
aef7f58c6d Merge pull request #12 from xuzeyu91/xuzeyu91-patch-2
Update README.en.md
2024-03-04 00:12:45 +08:00
zyxucp
52450efeb3 Update README.en.md 2024-03-04 00:12:31 +08:00
zyxucp
74d218d2d5 Merge pull request #11 from xuzeyu91/xuzeyu91-patch-1
Update README.md
2024-03-04 00:11:02 +08:00
zyxucp
f4a60b28ec Update README.md 2024-03-04 00:10:45 +08:00
zyxucp
798593bad3 fix 修改删除刷新事件 2024-03-04 00:08:23 +08:00
zyxucp
778e76b00b add 模型删除 2024-03-03 23:57:06 +08:00
zyxucp
55778562ae add 增加模型管理界面 2024-03-03 23:50:40 +08:00
zyxucp
66b5b42952 add 修改首页 2024-03-03 23:17:56 +08:00
zyxucp
3ff8a4f51b add 增加会话使用温度值 2024-03-03 23:00:26 +08:00
zyxucp
b2a46db524 add Temperature 2024-03-03 22:57:48 +08:00
zyxucp
8d66f4f34d Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-03 22:07:17 +08:00
zyxucp
199b546d56 fix 优化项目 2024-03-03 22:06:26 +08:00
zyxucp
bea6e5ef53 fix 封装kernel 2024-03-03 21:43:28 +08:00
zyxucp
3c17dc9943 add 增加注释 2024-03-03 21:16:56 +08:00
zyxucp
6d693ed340 fix 修改kernel类 2024-03-03 21:14:22 +08:00
zyxucp
f86b7aee95 fix 修改切分符号 2024-03-03 20:39:37 +08:00
zyxucp
fca2d98d51 fix 修改分隔符 2024-03-03 20:38:15 +08:00
zyxucp
2f3d3d7b55 add 增加插件代理 2024-03-03 01:55:55 +08:00
zyxucp
5250817d21 add 插件 2024-03-02 16:50:24 +08:00
zyxucp
6f966590d9 add 增加httpheader 2024-03-02 16:12:25 +08:00
zyxucp
da16cf73c9 fix 格式化代码 2024-03-02 14:41:06 +08:00
zyxucp
999cda2fc2 add API详情 2024-03-02 14:40:38 +08:00
zyxucp
28f5be071a add apilist 2024-03-02 13:51:22 +08:00
zyxucp
4ad8fb69c7 add 增加api列表 2024-03-02 13:42:41 +08:00
zyxucp
2cb5ab7f98 add 增加员工查询搜索 2024-03-02 12:31:25 +08:00
zyxucp
97b87f2adb Update README.en.md 2024-03-02 09:02:23 +08:00
zyxucp
d23c263f9f Update README.md 2024-03-02 09:00:34 +08:00
zyxucp
7f003afa2b Merge pull request #7 from itchangc/main
创建vector插件如果数据库没有则需要提供支持向量的数据库
2024-03-01 16:03:49 +08:00
自动畅
53d52dd17f Update Program.cs
创建vector插件如果数据库没有则需要提供支持向量的数据库
2024-03-01 15:20:55 +08:00
zyxucp
036d57ef05 fix 不知道为什么markdown没有 Content-Type,单独处理一下 2024-03-01 14:38:57 +08:00
zyxucp
b25e429687 fix 修改maxtoken 2024-03-01 12:15:21 +08:00
zyxucp
a3f87bf123 fix 修复维度和maxtoken问题 2024-03-01 12:10:11 +08:00
zyxucp
2cab253c4a fix 修复openai stream接口格式不正确问题 2024-03-01 12:09:27 +08:00
zyxucp
ed119f02e2 fix 修改message实体 2024-02-29 18:28:32 +08:00
zyxucp
f178fc16e9 style 样式调整 2024-02-29 18:23:50 +08:00
zyxucp
e4b2071689 fix SK还原到1.4 2024-02-29 17:49:43 +08:00
zyxucp
146df7ed1d fix 修改SK版本 2024-02-29 17:42:41 +08:00
zyxucp
7e8db0a3f8 add 修改气泡样式 2024-02-29 17:40:43 +08:00
zyxucp
e098922219 fix 修改聊天窗为气泡样式 2024-02-29 17:30:50 +08:00
zyxucp
16ca024c22 Merge branch 'main' into feature_plugin 2024-02-29 13:55:15 +08:00
zyxucp
a886e9dfb3 fix 修复兼容域名的情况 端口为0 2024-02-29 13:54:33 +08:00
zyxucp
5d6114c9ec update 升级SK 至1.5.0 和升级KM 2024-02-29 12:52:44 +08:00
zyxucp
e3b2e1f434 fix 修复没登陆时没有权限的问题 2024-02-29 12:41:50 +08:00
zyxucp
6145347d9b add 增加权限管理 2024-02-29 12:35:03 +08:00
zyxucp
49e694cbdc add 增加非管理员不允许操作系统设置 2024-02-29 12:00:22 +08:00
zyxucp
f860229993 fix 调整Util目录 2024-02-29 10:38:36 +08:00
zyxucp
f5d93baa17 fix 使用默认切片 2024-02-28 18:48:40 +08:00
zyxucp
76f58c43b0 Update README.md 2024-02-28 17:55:48 +08:00
zyxucp
db29fcc867 add 用户个人设置 2024-02-28 17:17:37 +08:00
zyxucp
2f361c23c7 add 修改登陆人名称 2024-02-28 14:50:04 +08:00
zyxucp
4e7ac6eb4b add 增加多用户登陆验证 2024-02-28 14:31:03 +08:00
zyxucp
58bca689c8 add 增加用户新增功能 2024-02-28 14:15:11 +08:00
zyxucp
e1b2aee33c add 新增用户 2024-02-28 13:58:41 +08:00
zyxucp
7a6e8525c5 add 增加用户新增页面 2024-02-28 12:59:12 +08:00
zyxucp
b8db68bed1 add 增加用户列表 2024-02-28 12:38:55 +08:00
zyxucp
1072f47467 fix 修改切片分段 2024-02-28 12:03:31 +08:00
zyxucp
cfb1d858c6 fix 修改文案 2024-02-27 16:00:56 +08:00
zyxucp
8e0e75aee6 fix 修复切片显示bug 2024-02-27 13:14:42 +08:00
zyxucp
1479c942e2 fix 修改维度 2024-02-27 12:57:14 +08:00
zyxucp
63b02b889c fix 修复Embedding格式错误 2024-02-27 12:26:21 +08:00
zyxucp
5a1de0e2cf add 增加Embedding 2024-02-27 11:43:00 +08:00
zyxucp
ad2a402521 add 修改代理增加端口 2024-02-27 09:56:49 +08:00
zyxucp
a14af93afc add 增加LLamaSharp本地模型对接 2024-02-27 01:14:12 +08:00
zyxucp
90630bca1d add 样式修改 2024-02-25 11:24:46 +08:00
zyxucp
bf1db38e2e fix 修改知识库分配查询逻辑 2024-02-22 22:42:08 +08:00
zyxucp
88e23768c6 Merge pull request #5 from LoganChi/patch-1
Update 404.razor
2024-02-22 20:02:07 +08:00
zyxucp
ea8366ca92 Merge pull request #4 from LoganChi/patch-2
Create 404.razor.css
2024-02-22 20:01:55 +08:00
zyxucp
8e1f07415f Merge pull request #3 from LoganChi/patch-3
Update App.razor
2024-02-22 20:00:48 +08:00
LoganChi
0b62e773eb Update App.razor 2024-02-22 15:36:26 +08:00
LoganChi
1c4b1e2d2f Create 404.razor.css
an intersting 404 page
2024-02-22 15:33:46 +08:00
LoganChi
0e53dd3854 Update 404.razor 2024-02-22 15:30:54 +08:00
zyxucp
c2fa13b6ab Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-02-22 12:41:21 +08:00
zyxucp
837c9a1444 add 增加配置文件示例 2024-02-22 11:31:55 +08:00
zyxucp
08ec4f6b7e Update README.md 2024-02-21 18:22:08 +08:00
zyxucp
f5a8076da3 fix 修改返回json请求头 2024-02-20 19:17:40 +08:00
zyxucp
cf8c935694 fix 修改API流式输出 2024-02-20 19:15:50 +08:00
zyxucp
67fd7d952a add 增加接口流式输出 2024-02-20 18:45:41 +08:00
zyxucp
b7a3ad6fe7 update 更新SK与KM版本 2024-02-20 15:55:05 +08:00
zyxucp
0dabd2ee58 fix 修复openai地址为IP端口时正则匹配不到的问题 2024-02-20 11:24:31 +08:00
zyxucp
e841fc6282 fix 处理openai地址是IP端口的情况 2024-02-20 11:15:02 +08:00
zyxucp
ab45a46fc0 fix 修复正则匹配不到IP和端口的问题 2024-02-20 11:10:34 +08:00
zyxucp
34c06b1dd1 Merge branch 'feature_openai对外接口' 2024-02-18 23:53:23 +08:00
zyxucp
50415b12c0 add 增加描述 2024-02-18 23:49:30 +08:00
zyxucp
61509154c6 add 增加会话嵌入页面 2024-02-18 23:24:27 +08:00
zyxucp
3d5be56089 Update README.md 2024-02-18 16:02:30 +08:00
zyxucp
a0da9c223b fix 取消api的markdown格式化 2024-02-18 15:52:00 +08:00
zyxucp
172755081f add 新增对外接口 2024-02-18 15:46:21 +08:00
zyxucp
1da43f2cee fix 调整Pro框架Service结构 2024-02-18 14:20:46 +08:00
zyxucp
8b77ea4438 add 增加外部API配置页面 2024-02-18 14:05:15 +08:00
zyxucp
b6039c0da3 Delete AntSK.db 2024-02-18 10:46:05 +08:00
zyxucp
1cda01d5b3 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-02-15 16:43:41 +08:00
zyxucp
6bd2ef4131 add 添加一点延迟,让对话输出更平缓 2024-02-15 16:41:47 +08:00
zyxucp
96e1ca6b34 Update README.en.md 2024-02-15 13:39:39 +08:00
zyxucp
6620a74447 Update README.md 2024-02-15 13:38:46 +08:00
zyxucp
fb7a85b9cf Update README.md 2024-02-15 13:38:25 +08:00
zyxucp
b7532676c5 add 新增系统账号,需要输入账号密码才可访问 2024-02-15 13:34:32 +08:00
zyxucp
8cd6940311 update 修改登陆页 2024-02-15 12:36:52 +08:00
zyxucp
2de851f5b1 fix 如果模板为空给一个默认提示词 2024-02-14 20:55:25 +08:00
zyxucp
e8fd055349 add 增加首页 2024-02-10 00:54:46 +08:00
zyxucp
7b92373f54 fix 提前function让代码更整洁 2024-02-10 00:22:36 +08:00
zyxucp
03b937432f add 增加多轮会话,会话总结 2024-02-10 00:19:56 +08:00
zyxucp
bd9798ad17 fix 处理知识库markdown样式 2024-02-09 23:23:17 +08:00
zyxucp
030abf1059 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-02-09 23:05:30 +08:00
zyxucp
91e633700a add 增加应用标签 2024-02-09 23:03:49 +08:00
zyxucp
6eb034967c add 增加聊天流式输出 2024-02-09 22:49:44 +08:00
130 changed files with 5217 additions and 455 deletions

View File

@@ -5,18 +5,29 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DocumentationFile>AntSK.Domain.xml</DocumentationFile>
<NoWarn>CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="8.1.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="LLamaSharp.kernel-memory" Version="0.10.0" />
<PackageReference Include="LLamaSharp.semantic-kernel" Version="0.10.0" />
<PackageReference Include="MarkdownSharp" Version="2.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.137" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
<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.3.0" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.3.0" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.26.240121.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.26.240121.1" />
<PackageReference Include="LLamaSharp" Version="0.10.0" />
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.10.0" />
</ItemGroup>
</Project>

View File

@@ -32,6 +32,36 @@
瞬时
</summary>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.GetKernel(System.String,System.String)">
<summary>
获取kernel实例依赖注入不好按每个用户去Import不同的插件所以每次new一个新的kernel
</summary>
<param name="modelId"></param>
<param name="apiKey"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.ImportFunctionsByApp(AntSK.Domain.Repositories.Apps,Microsoft.SemanticKernel.Kernel)">
<summary>
根据app配置的插件导入插件
</summary>
<param name="app"></param>
<param name="_kernel"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.RegisterPluginsWithKernel(Microsoft.SemanticKernel.Kernel)">
<summary>
注册默认插件
</summary>
<param name="kernel"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.HistorySummarize(Microsoft.SemanticKernel.Kernel,System.String,System.String)">
<summary>
会话总结
</summary>
<param name="_kernel"></param>
<param name="questions"></param>
<param name="history"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTOList``1(System.Object)">
<summary>
Entity集合转DTO集合
@@ -57,6 +87,11 @@
<param name="result"></param>
<returns></returns>
</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开始
@@ -82,6 +117,46 @@
pg链接字符串
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.Name">
<summary>
接口名称
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.Describe">
<summary>
接口描述
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.Url">
<summary>
接口地址
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.Method">
<summary>
请求方法
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.Query">
<summary>
QueryString参数
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.JsonBody">
<summary>
jsonBody 实体
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.InputPrompt">
<summary>
入参提示词
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apis.OutputPrompt">
<summary>
返回提示词
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Name">
<summary>
名称
@@ -92,16 +167,41 @@
描述
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Icon">
<summary>
图标
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Type">
<summary>
类型
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Temperature">
<summary>
温度
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Prompt">
<summary>
提示词
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.ApiFunctionList">
<summary>
插件列表
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.KmsIdList">
<summary>
知识库ID列表
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.SecretKey">
<summary>
API调用秘钥
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.KmsDetails.FileName">
<summary>
文件名称
@@ -142,6 +242,21 @@
会话模型
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Kmss.MaxTokensPerParagraph">
<summary>
每个段落的最大标记数。
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Kmss.MaxTokensPerLine">
<summary>
每行,也就是每句话的最大标记数
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Kmss.OverlappingTokens">
<summary>
段落之间重叠标记的数量。
</summary>
</member>
<member name="M:AntSK.Domain.Repositories.Base.Repository`1.GetList">
<summary>
获取所有list
@@ -399,6 +514,51 @@
sqlserver连接
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.AIModelType">
<summary>
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.EndPoint">
<summary>
模型地址
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.ModelName">
<summary>
模型名称
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.ModelKey">
<summary>
模型秘钥
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Users.No">
<summary>
工号,用于登陆
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Users.Password">
<summary>
密码
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Users.Name">
<summary>
名称
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Users.Describe">
<summary>
备注
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Users.MenuRole">
<summary>
菜单权限
</summary>
</member>
<member name="M:AntSK.Domain.Utils.ConvertUtils.IsNull(System.Object)">
<summary>
判断是否为空为空返回true
@@ -489,5 +649,11 @@
<param name="stream"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Utils.RepoFiles.SamplePluginsPath">
<summary>
Scan the local folders from the repo, looking for "samples/plugins" folder.
</summary>
<returns>The full path to samples/plugins</returns>
</member>
</members>
</doc>

View File

@@ -1,4 +1,6 @@
using AntSK.Domain.Domain.Dto;
using Microsoft.KernelMemory.Configuration;
using Microsoft.KernelMemory;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,6 +11,7 @@ namespace AntSK.Domain.Domain.Interface
{
public interface IKMService
{
MemoryServerless GetMemory(SearchClientConfig searchClientConfig = null, TextPartitioningOptions textPartitioningOptions = null);
Task<List<KMFile>> GetDocumentByFileID(string fileid);
}
}

View File

@@ -0,0 +1,17 @@
using AntSK.Domain.Repositories;
using Microsoft.SemanticKernel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Interface
{
public interface IKernelService
{
Kernel GetKernel(string modelId = null, string apiKey = null);
void ImportFunctionsByApp(Apps app, Kernel _kernel);
Task<string> HistorySummarize(Kernel _kernel, string questions, string history);
}
}

View File

@@ -3,14 +3,71 @@ using AntSK.Domain.Domain.Interface;
using Microsoft.KernelMemory;
using AntSK.Domain.Utils;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Options;
using Microsoft.KernelMemory.ContentStorage.DevTools;
using Microsoft.KernelMemory.FileSystem.DevTools;
using Microsoft.KernelMemory.Postgres;
using System.Net.Http;
using Microsoft.Extensions.Options;
using Microsoft.KernelMemory.Configuration;
namespace AntSK.Domain.Domain.Service
{
[ServiceDescription(typeof(IKMService), ServiceLifetime.Scoped)]
public class KMService(MemoryServerless _memory) : IKMService
public class KMService(
IOptions<PostgresConfig> postgresOptions
) : IKMService
{
public MemoryServerless GetMemory(SearchClientConfig searchClientConfig=null, TextPartitioningOptions textPartitioningOptions=null)
{
var handler = new OpenAIHttpClientHandler();
var httpClient = new HttpClient(handler);
if (searchClientConfig.IsNull())
{
searchClientConfig = new SearchClientConfig
{
MaxAskPromptSize = 2048,
MaxMatchesCount = 3,
AnswerTokens = 1000,
EmptyAnswer = "知识库未搜索到相关内容"
};
}
if (textPartitioningOptions.IsNull())
{
textPartitioningOptions = new TextPartitioningOptions
{
MaxTokensPerLine = 99,
MaxTokensPerParagraph = 299,
OverlappingTokens = 47
};
}
var memory = new KernelMemoryBuilder()
.WithPostgresMemoryDb(postgresOptions.Value)
.WithSimpleFileStorage(new SimpleFileStorageConfig { StorageType = FileSystemTypes.Volatile, Directory = "_files" })
.WithSearchClientConfig(searchClientConfig)
//如果用本地模型需要设置token小一点。
.WithCustomTextPartitioningOptions(textPartitioningOptions)
.WithOpenAITextGeneration(new OpenAIConfig()
{
APIKey = OpenAIOption.Key,
TextModel = OpenAIOption.Model
}, null, httpClient)
.WithOpenAITextEmbeddingGeneration(new OpenAIConfig()
{
APIKey = OpenAIOption.Key,
EmbeddingModel = OpenAIOption.EmbeddingModel
}, null, false, httpClient)
.Build<MemoryServerless>();
return memory;
}
public async Task<List<KMFile>> GetDocumentByFileID(string fileid)
{
var _memory = GetMemory();
var memories = await _memory.ListIndexesAsync();
var memoryDbs = _memory.Orchestrator.GetMemoryDbs();
List<KMFile> docTextList = new List<KMFile>();
@@ -19,23 +76,19 @@ namespace AntSK.Domain.Domain.Service
{
foreach (var memoryDb in memoryDbs)
{
var list = memoryDb.GetListAsync(memoryIndex.Name, null, 100, true);
await foreach (var item in list)
var items = await memoryDb.GetListAsync(memoryIndex.Name, new List<MemoryFilter>() { new MemoryFilter().ByDocument(fileid) }, 100, true).ToListAsync();
foreach (var item in items)
{
if (item.Id.Contains(fileid))
KMFile file = new KMFile()
{
KMFile file = new KMFile()
{
Text = item.Payload.FirstOrDefault(p => p.Key == "text").Value.ConvertToString(),
Url= item.Payload.FirstOrDefault(p => p.Key == "url").Value.ConvertToString(),
LastUpdate= item.Payload.FirstOrDefault(p => p.Key == "last_update").Value.ConvertToString(),
Schema = item.Payload.FirstOrDefault(p => p.Key == "schema").Value.ConvertToString(),
File = item.Payload.FirstOrDefault(p => p.Key == "file").Value.ConvertToString(),
};
docTextList.Add(file);
}
Text = item.Payload.FirstOrDefault(p => p.Key == "text").Value.ConvertToString(),
Url = item.Payload.FirstOrDefault(p => p.Key == "url").Value.ConvertToString(),
LastUpdate = item.Payload.FirstOrDefault(p => p.Key == "last_update").Value.ConvertToString(),
Schema = item.Payload.FirstOrDefault(p => p.Key == "schema").Value.ConvertToString(),
File = item.Payload.FirstOrDefault(p => p.Key == "file").Value.ConvertToString(),
};
docTextList.Add(file);
}
}
}

View File

@@ -0,0 +1,157 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.Core;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Service
{
[ServiceDescription(typeof(IKernelService), ServiceLifetime.Scoped)]
public class KernelService(
IApis_Repositories _apis_Repositories
) : IKernelService
{
/// <summary>
/// 获取kernel实例依赖注入不好按每个用户去Import不同的插件所以每次new一个新的kernel
/// </summary>
/// <param name="modelId"></param>
/// <param name="apiKey"></param>
/// <returns></returns>
public Kernel GetKernel(string modelId=null,string apiKey=null)
{
var handler = new OpenAIHttpClientHandler();
var httpClient = new HttpClient(handler);
httpClient.Timeout = TimeSpan.FromMinutes(5);
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: modelId!=null? modelId : OpenAIOption.Model,
apiKey: apiKey!=null? apiKey: OpenAIOption.Key,
httpClient: httpClient)
.Build();
RegisterPluginsWithKernel(kernel);
return kernel;
}
/// <summary>
/// 根据app配置的插件导入插件
/// </summary>
/// <param name="app"></param>
/// <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");
{
foreach (var api in apiList)
{
switch (api.Method)
{
case HttpMethodType.Get:
functions.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"))
{
var headerArray = header.Split(":");
if (headerArray.Length == 2)
{
request.AddHeader(headerArray[0], headerArray[1]);
}
}
//这里应该还要处理一次参数提取,等后面再迭代
foreach (var query in api.Query.Split("\n"))
{
var queryArray = query.Split("=");
if (queryArray.Length == 2)
{
request.AddQueryParameter(queryArray[0], queryArray[1]);
}
}
var result = client.Execute(request);
return result.Content;
}
catch (System.Exception ex)
{
return "调用失败:" + ex.Message;
}
}, api.Name, $"{api.Describe}"));
break;
case HttpMethodType.Post:
functions.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"))
{
var headerArray = header.Split(":");
if (headerArray.Length == 2)
{
request.AddHeader(headerArray[0], headerArray[1]);
}
}
//这里应该还要处理一次参数提取,等后面再迭代
request.AddJsonBody(api.JsonBody);
var result = client.Execute(request);
return result.Content;
}
catch (System.Exception ex)
{
return "调用失败:" + ex.Message;
}
}, api.Name, $"{api.Describe}"));
break;
}
}
_kernel.ImportPluginFromFunctions("ApiFunctions", functions);
}
}
/// <summary>
/// 注册默认插件
/// </summary>
/// <param name="kernel"></param>
void RegisterPluginsWithKernel(Kernel kernel)
{
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
kernel.ImportPluginFromPromptDirectory(Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
}
/// <summary>
/// 会话总结
/// </summary>
/// <param name="_kernel"></param>
/// <param name="questions"></param>
/// <param name="history"></param>
/// <returns></returns>
public async Task<string> HistorySummarize(Kernel _kernel,string questions, string history)
{
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}"; ;
return msg;
}
}
}

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.Model.Enum
{
public enum AIModelType
{
Chat = 1,
Embedding = 2,
}
}

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.Model
{
public enum HttpMethodType
{
Get = 1,
Post = 2,
}
}

View File

@@ -9,9 +9,13 @@ namespace AntSK.Domain.Model
public class MessageInfo
{
public string ID { get; set; } = "";
public string Questions { get; set; } = "";
public string Answers { get; set; } = "";
public string Context { get; set; } = "";
public string HtmlAnswers { get; set; } = "";
/// <summary>
/// 发送是true 接收是false
/// </summary>
public bool IsSend { get; set; } = false;
public DateTime CreateTime { get; set; }
}

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.Options
{
public class LLamaSharpOption
{
public static string Chat { get; set; }
public static string Embedding { get; set; }
}
}

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.Options
{
public class LoginOption
{
public static string User { get; set; }
public static string Password { get; set; }
}
}

View File

@@ -0,0 +1,67 @@
using AntSK.Domain.Model;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Repositories
{
[SugarTable("Apis")]
public partial class Apis
{
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// 接口名称
/// </summary>
[Required]
public string Name { get; set; }
/// <summary>
/// 接口描述
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 接口地址
/// </summary>
[Required]
public string Url { get; set; }
/// <summary>
/// 请求方法
/// </summary>
[Required]
public HttpMethodType Method { get; set; }
[SugarColumn(ColumnDataType = "varchar(2000)")]
public string? Header { get; set; }
/// <summary>
/// QueryString参数
/// </summary>
[SugarColumn(ColumnDataType = "varchar(2000)")]
public string? Query { get; set; }
/// <summary>
/// jsonBody 实体
/// </summary>
[SugarColumn(ColumnDataType = "varchar(8000)")]
public string? JsonBody { get; set; }
/// <summary>
/// 入参提示词
/// </summary>
[Required]
[SugarColumn(ColumnDataType = "varchar(2000)")]
public string InputPrompt { get; set; }
/// <summary>
/// 返回提示词
/// </summary>
[Required]
[SugarColumn(ColumnDataType = "varchar(2000)")]
public string OutputPrompt { get; set; }
}
}

View File

@@ -0,0 +1,16 @@

using AntSK.Domain.Repositories.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Common.DependencyInjection;
namespace AntSK.Domain.Repositories
{
[ServiceDescription(typeof(IApis_Repositories), ServiceLifetime.Scoped)]
public class Apis_Repositories : Repository<Apis>, IApis_Repositories
{
}
}

View File

@@ -0,0 +1,13 @@
using AntSK.Domain.Repositories.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Repositories
{
public interface IApis_Repositories : IRepository<Apis>
{
}
}

View File

@@ -25,19 +25,41 @@ namespace AntSK.Domain.Repositories
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 图标
/// </summary>
[Required]
public string Icon { get; set; }
/// <summary>
/// 类型
/// </summary>
[Required]
public string Type { get; set; }
/// <summary>
/// 温度
/// </summary>
public double Temperature { get; set; }=70f;
/// <summary>
/// 提示词
/// </summary>
public string? Prompt { get; set; }
/// <summary>
/// 插件列表
/// </summary>
public string? ApiFunctionList { get; set; }
/// <summary>
/// 知识库ID列表
/// </summary>
public string? KmsIdList { get; set; }
/// <summary>
/// API调用秘钥
/// </summary>
public string? SecretKey { get; set; }
}
}

View File

@@ -28,5 +28,22 @@ namespace AntSK.Domain.Repositories
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 每个段落的最大标记数。
/// </summary>
public int MaxTokensPerParagraph { get; set; } = 1000;
/// <summary>
/// 每行,也就是每句话的最大标记数
/// </summary>
public int MaxTokensPerLine { get; set; } = 300;
/// <summary>
/// 段落之间重叠标记的数量。
/// </summary>
public int OverlappingTokens { get; set; } = 100;
}
}

View File

@@ -0,0 +1,40 @@
using AntSK.Domain.Model.Enum;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Repositories
{
[SugarTable("AIModels")]
public partial class AIModels
{
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// 模型类型
/// </summary>
[Required]
public AIModelType AIModelType { get; set; }
/// <summary>
/// 模型地址
/// </summary>
[Required]
public string EndPoint { get; set; }
/// <summary>
/// 模型名称
/// </summary>
[Required]
public string ModelName { get; set; }
/// <summary>
/// 模型秘钥
/// </summary>
[Required]
public string ModelKey { get; set; }
[Required]
public string ModelDescription { get; set; }
}
}

View File

@@ -0,0 +1,16 @@

using AntSK.Domain.Repositories.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Common.DependencyInjection;
namespace AntSK.Domain.Repositories
{
[ServiceDescription(typeof(IAIModels_Repositories), ServiceLifetime.Scoped)]
public class AIModels_Repositories : Repository<AIModels>, IAIModels_Repositories
{
}
}

View File

@@ -0,0 +1,13 @@
using AntSK.Domain.Repositories.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Repositories
{
public interface IAIModels_Repositories : IRepository<AIModels>
{
}
}

View File

@@ -0,0 +1,13 @@
using AntSK.Domain.Repositories.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Repositories
{
public interface IUsers_Repositories : IRepository<Users>
{
}
}

View File

@@ -0,0 +1,47 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Repositories
{
[SugarTable("Users")]
public partial class Users
{
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// 工号,用于登陆
/// </summary>
[Required]
public string No { get; set; }
/// <summary>
/// 密码
/// </summary>
[Required]
public string Password { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required]
public string Name { get; set; }
/// <summary>
/// 备注
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 菜单权限
/// </summary>
[Required]
public string MenuRole { get; set; }
}
}

View File

@@ -0,0 +1,16 @@

using AntSK.Domain.Repositories.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Common.DependencyInjection;
namespace AntSK.Domain.Repositories
{
[ServiceDescription(typeof(IUsers_Repositories), ServiceLifetime.Scoped)]
public class Users_Repositories : Repository<Users>, IUsers_Repositories
{
}
}

View File

@@ -3,7 +3,7 @@ using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AntSK.Utils
namespace AntSK.Domain.Utils
{
public class LongToDateTimeConverter : JsonConverter<DateTime>
{

View File

@@ -13,13 +13,25 @@ namespace AntSK.Domain.Utils
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
UriBuilder uriBuilder;
Regex regex = new Regex(@"(https?)://([^/]+)/(.*)");
Regex regex = new Regex(@"(https?)://([^/:]+)(:\d+)?/(.*)");
Match match = regex.Match(OpenAIOption.EndPoint);
string xieyi = match.Groups[1].Value;
string host = match.Groups[2].Value;
string route = match.Groups[3].Value;
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" && request.Content != null)
{
string requestBody = await request.Content.ReadAsStringAsync();
//便于调试查看请求prompt
Console.WriteLine(requestBody);
}
if (match.Success)
{
string xieyi = match.Groups[1].Value;
string host = match.Groups[2].Value;
string port = match.Groups[3].Value; // 可选的端口号
string route = match.Groups[4].Value;
// 如果port不为空它将包含冒号所以你可能需要去除它
port = string.IsNullOrEmpty(port) ? port : port.Substring(1);
// 拼接host和端口号
var hostnew = string.IsNullOrEmpty(port) ? host : $"{host}:{port}";
switch (request.RequestUri.LocalPath)
{
case "/v1/chat/completions":
@@ -27,10 +39,15 @@ namespace AntSK.Domain.Utils
uriBuilder = new UriBuilder(request.RequestUri)
{
// 这里是你要修改的 URL
Scheme = $"{xieyi}://{host}/",
Host = host,
Scheme = $"{xieyi}://{hostnew}/",
Host = host,
Path = route + "v1/chat/completions",
};
if (port.ConvertToInt32() != 0)
{
uriBuilder.Port = port.ConvertToInt32();
}
request.RequestUri = uriBuilder.Uri;
break;
@@ -42,10 +59,15 @@ namespace AntSK.Domain.Utils
Host = host,
Path = route + "v1/embeddings",
};
if (port.ConvertToInt32() != 0)
{
uriBuilder.Port = port.ConvertToInt32();
}
request.RequestUri = uriBuilder.Uri;
break;
}
}
// 接着,调用基类的 SendAsync 方法将你的修改后的请求发出去
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

View File

@@ -0,0 +1,19 @@
using BCrypt.Net;
namespace AntSK.Domain.Utils
{
public class PasswordUtil
{
public static string HashPassword(string password)
{
// 默认的工作因子是10可以根据你的需求调整以增加散列的复杂度
return BCrypt.Net.BCrypt.HashPassword(password);
}
// 验证密码是否匹配散列值
public static bool VerifyPassword(string password, string hashedPassword)
{
return BCrypt.Net.BCrypt.Verify(password, hashedPassword);
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Utils
{
public static class RepoFiles
{
/// <summary>
/// Scan the local folders from the repo, looking for "samples/plugins" folder.
/// </summary>
/// <returns>The full path to samples/plugins</returns>
public static string SamplePluginsPath()
{
string Parent = AppDomain.CurrentDomain.BaseDirectory;
string Folder = "plugins";
bool SearchPath(string pathToFind, out string result, int maxAttempts = 10)
{
var currDir = Path.GetFullPath(Assembly.GetExecutingAssembly().Location);
bool found;
do
{
result = Path.Join(currDir, pathToFind);
found = Directory.Exists(result);
currDir = Path.GetFullPath(Path.Combine(currDir, ".."));
} while (maxAttempts-- > 0 && !found);
return found;
}
if (!SearchPath(Parent + Path.DirectorySeparatorChar + Folder, out string path)
&& !SearchPath(Folder, out path))
{
throw new YourAppException("Plugins directory not found. The app needs the plugins from the repo to work.");
}
return path;
}
}
public class YourAppException : Exception
{
public YourAppException() : base()
{
}
public YourAppException(string message) : base(message)
{
}
public YourAppException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

View File

@@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.Domain", "AntSK.Domai
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docker", "Docker", "{9F2E193A-5F9D-4C82-B591-CB133EEB59F0}"
ProjectSection(SolutionItems) = preProject
docker-compose.yml = docker-compose.yml
Dockerfile = Dockerfile
EndProjectSection
EndProject

View File

@@ -5,11 +5,14 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DocumentationFile>AntSK.xml</DocumentationFile>
<NoWarn>CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AntDesign.Charts" Version="0.5.1" />
<PackageReference Include="AntDesign.ProLayout" Version="0.17.3" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.2" />
<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" />
</ItemGroup>
@@ -19,7 +22,9 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Pages\Setting\" />
<None Update="plugins\KMSPlugin\Ask\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -16,6 +16,85 @@
</summary>
<returns></returns>
</member>
<member name="M:AntSK.Controllers.LLamaSharpController.chat(AntSK.Models.OpenAIModel)">
<summary>
本地会话接口
</summary>
<returns></returns>
</member>
<member name="M:AntSK.Controllers.LLamaSharpController.embedding(AntSK.Models.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.Models.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="msg"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.Chat.HistorySummarize(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="msg"></param>
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.OpenChat.HistorySummarize(System.String)">
<summary>
历史会话的会话总结
</summary>
<param name="questions"></param>
<returns></returns>
</member>
<member name="T:AntSK.Pages.KmsPage.KmsDetail.UrlModel">
<summary>
根据文档ID获取文档
@@ -23,5 +102,41 @@
<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.Models.OpenAIModel)">
<summary>
历史会话的会话总结
</summary>
<param name="questions"></param>
<param name="msg"></param>
<returns></returns>
</member>
</members>
</doc>

View File

@@ -1,13 +1,32 @@
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<CascadingValue Value="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
</CascadingValue>
</Found>
<NotFound>
<LayoutView Layout="@typeof(BasicLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
<AntContainer />
@inject NavigationManager NavigationManager
@using Microsoft.AspNetCore.Components.Authorization
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<CascadingValue Value="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
</CascadingValue>
</Found>
<NotFound>
<LayoutView Layout="@typeof(BasicLayout)">
<AntSK.Pages.Exception._404/>
</LayoutView>
</NotFound>
</Router>
<AntContainer />
</CascadingAuthenticationState>
@code {
private RenderFragment RedirectToLogin => builder =>
{
var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
if (string.IsNullOrWhiteSpace(returnUrl))
{
NavigationManager.NavigateTo("user/login");
}
else
{
NavigationManager.NavigateTo($"user/login?returnUrl={Uri.EscapeDataString(returnUrl)}", true);
}
};
}

View File

@@ -1,11 +1,13 @@
@namespace AntSK.Components
@using Microsoft.AspNetCore.Authorization
@inherits AntDomComponentBase
<Space Class="@ClassMapper.Class" Size="@("24")">
<Space Class="@ClassMapper.Class" Size="@("26")">
<SpaceItem>
<AvatarDropdown Name="@_currentUser.Name"
<AvatarDropdown Name="@context.Identity.Name"
Avatar="@_currentUser.Avatar"
MenuItems="@AvatarMenuItems"
OnItemSelected="HandleSelectUser" />
</SpaceItem>
</Space>

View File

@@ -6,6 +6,10 @@ using System.Linq;
using System.Threading.Tasks;
using AntSK.Models;
using AntSK.Services;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using AntSK.Services.Auth;
using AntSK.Domain.Options;
namespace AntSK.Components
{
@@ -38,7 +42,6 @@ namespace AntSK.Components
public AvatarMenuItem[] AvatarMenuItems { get; set; } = new AvatarMenuItem[]
{
new() { Key = "center", IconType = "user", Option = "个人中心"},
new() { Key = "setting", IconType = "setting", Option = "个人设置"},
new() { IsDivider = true },
new() { Key = "logout", IconType = "logout", Option = "退出登录"}
@@ -50,6 +53,11 @@ namespace AntSK.Components
[Inject] protected IProjectService ProjectService { get; set; }
[Inject] protected MessageService MessageService { get; set; }
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
[Inject] protected MessageService? Message { get; set; }
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
@@ -73,11 +81,15 @@ namespace AntSK.Components
{
switch (item.Key)
{
case "center":
NavigationManager.NavigateTo("/account/center");
break;
case "setting":
NavigationManager.NavigateTo("/account/settings");
if (context.Identity.Name != LoginOption.User)
{
NavigationManager.NavigateTo("/setting/user/info/" + context.Identity.Name);
}
else
{
_ = Message.Info("管理员无需设置", 2);
}
break;
case "logout":
NavigationManager.NavigateTo("/user/login");

View File

@@ -0,0 +1,47 @@
using AntSK.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using AntSK.Domain.Utils;
using AntSK.Services.LLamaSharp;
namespace AntSK.Controllers
{
[ApiController]
public class LLamaSharpController(ILLamaSharpService _lLamaSharpService) : ControllerBase
{
/// <summary>
/// 本地会话接口
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("llama/v1/chat/completions")]
public async Task chat(OpenAIModel model)
{
Console.WriteLine("开始llama/v1/chat/completions");
if (model.stream)
{
await _lLamaSharpService.ChatStream(model, HttpContext);
}
else
{
await _lLamaSharpService.Chat(model, HttpContext);
}
Console.WriteLine("结束llama/v1/chat/completions");
}
/// <summary>
/// 本地嵌入接口
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
[Route("llama/v1/embeddings")]
public async Task embedding(OpenAIEmbeddingModel model)
{
Console.WriteLine("开始llama/v1/embeddings");
await _lLamaSharpService.Embedding(model,HttpContext);
Console.WriteLine("结束llama/v1/embeddings");
}
}
}

View File

@@ -0,0 +1,31 @@
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using AntSK.Models;
using AntSK.Models.OpenAPI;
using AntSK.Services.OpenApi;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace AntSK.Controllers
{
/// <summary>
/// 对外接口
/// </summary>
[ApiController]
public class OpenController(IOpenApiService _openApiService) : ControllerBase
{
/// <summary>
/// 对话接口
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("api/v1/chat/completions")]
public async Task chat(OpenAIModel model)
{
string sk = HttpContext.Request.Headers["Authorization"].ConvertToString();
await _openApiService.Chat(model,sk, HttpContext);
}
}
}

View File

@@ -1,4 +1,9 @@
@namespace AntSK
@using System.Security.Claims
@using AntSK.Services.Auth
@using Microsoft.AspNetCore.Components.Authorization
@using AntSK.Domain.Options
@using AntSK.Domain.Repositories
@inherits LayoutComponentBase
<AntDesign.ProLayout.BasicLayout
@@ -21,11 +26,28 @@
private MenuDataItem[] _menuData = { };
[Inject] public HttpClient HttpClient { get; set; }
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_menuData = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
//菜单权限控制
var menuList = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
if ((bool)context?.Identity.IsAuthenticated)
{
if (context.Identity.Name == LoginOption.User)
{
_menuData = menuList;
}
else
{
var userMenuList = _users_Repositories.GetFirst(p => p.No == context.Identity.Name).MenuRole.Split(",").ToList();
//非管理员用户不允许操作系统设置
_menuData = menuList.Where(p => p.Key != "setting" && userMenuList.Contains(p.Key)).ToArray();
}
}
}

View File

@@ -0,0 +1,8 @@
@namespace AntSK
@inherits LayoutComponentBase
@Body
@code {
}

View File

@@ -11,14 +11,14 @@
<div class="header">
<a>
<img alt="logo" class="logo" src="assets/logo.svg" />
<span class="title">Ant Design</span>
<span class="title">AntSK</span>
</a>
</div>
<div class="desc">Ant Design Blazor</div>
<div class="desc"> </div>
</div>
@Body
</div>
<FooterView Copyright="2021 Ant Design Blazor" Links="Links"></FooterView>
<FooterView Copyright="许泽宇的技术分享" Links="Links"></FooterView>
</div>
@code

View File

@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Models
{
public class OpenAIModel
{
public bool stream { get; set; } = false;
public List<OpenAIMessage> messages { get; set; }
}
public class OpenAIMessage
{
public string role { get; set; }
public string content { get; set; }
}
public class OpenAIEmbeddingModel
{
public List<string> input { get; set; }
}
}

View File

@@ -0,0 +1,67 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace AntSK.Models.OpenAPI
{
public class OpenAIResult
{
public string id { get; set; } = Guid.NewGuid().ToString();
[JsonProperty("object")]
public string obj { get; set; } = "chat.completion";
public List<ChoicesModel> choices { get; set; }
public long created { get; set; }
}
public class ChoicesModel
{
public string finish_reason { get; set; } = "stop";
public int index { get; set; } = 0;
public OpenAIMessage message { get; set; }
}
public class OpenAIEmbeddingResult
{
[JsonProperty("object")]
public string obj { get; set; } = "list";
public string model { get; set; } = "ada";
public UsageModel usage { get; set; } = new UsageModel();
public List<DataModel> data { get; set; } = new List<DataModel>() { new DataModel() };
}
public class UsageModel
{
public long prompt_tokens { get; set; } = 0;
public long total_tokens { get; set; } = 0;
}
public class DataModel
{
[JsonProperty("object")]
public string obj { get; set; } = "embedding";
public int index { get; set; } = 0;
public List<float> embedding { get; set; }
}
public class OpenAIStreamResult
{
public string id { get; set; } = Guid.NewGuid().ToString();
[JsonProperty("object")]
public string obj { get; set; } = "chat.completion";
public List<StreamChoicesModel> choices { get; set; }
public long created { get; set; }
}
public class StreamChoicesModel
{
public int index { get; set; } = 0;
public OpenAIMessage delta { get; set; }
}
}

View File

@@ -0,0 +1,43 @@
using AntDesign;
namespace AntSK.Models
{
public class LayoutModel
{
public static FormItemLayout _formItemLayout = new FormItemLayout
{
LabelCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 7 },
},
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 12 },
Md = new EmbeddedProperty { Span = 10 },
}
};
public static FormItemLayout _submitFormLayout = new FormItemLayout
{
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
}
};
public static ListGridType _listGridType = new ListGridType
{
Gutter = 16,
Xs = 1,
Sm = 2,
Md = 3,
Lg = 3,
Xl = 4,
Xxl = 4
};
}
}

View File

@@ -1,6 +1,6 @@
using System;
using System.Text.Json.Serialization;
using AntSK.Utils;
using AntSK.Domain.Utils;
namespace AntSK.Models
{

View File

@@ -0,0 +1,76 @@
@namespace AntSK.Pages.ApiPage
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model
@page "/plugins/api/add"
@page "/plugins/api/add/{ApiId}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="新增API">
<ChildContent>
<Card>
<Form Model="@_apiModel"
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="注意" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<h1>API插件需要使用FunctionCall能力建议使用GPT4国产模型可能不太行</h1>
</FormItem>
<FormItem Label="Api名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入Api名称" @bind-Value="@context.Name" />
</FormItem>
<FormItem Label="Api描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入Api描述" @bind-Value="@context.Describe" />
</FormItem>
<FormItem Label="Url地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入Url地址" @bind-Value="@context.Url" />
</FormItem>
<FormItem Label="HttpHeader" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<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>
</FormItem>
@if (context.Method == HttpMethodType.Get)
{
<FormItem Label="Query" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea Placeholder="一个一行,以等号分割。例如:name=a" @bind-Value="@context.Query" MinRows="5" />
</FormItem>
}
else if (context.Method == HttpMethodType.Post)
{
<FormItem Label="JsonBody" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea Placeholder="请输入JsonBody" @bind-Value="@context.JsonBody" MinRows="5" />
</FormItem>
}
<FormItem Label="入参提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea Placeholder="请输入入参提示词" @bind-Value="@context.InputPrompt" MinRows="5" />
</FormItem>
<FormItem Label="返回提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea Placeholder="请输入返回提示词" @bind-Value="@context.OutputPrompt" MinRows="5" />
</FormItem>
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" HtmlType="submit">
保存
</Button>
<Button OnClick="Back">
返回
</Button>
</FormItem>
</Form>
</Card>
</ChildContent>
</PageContainer>
<style>
.avatar-uploader > .ant-upload {
width: 128px;
height: 128px;
}
</style>

View File

@@ -0,0 +1,72 @@
using AntDesign;
using Microsoft.AspNetCore.Components;
using AntSK.Domain.Repositories;
using AntSK.Models;
using System.IO;
using System.Text.RegularExpressions;
namespace AntSK.Pages.ApiPage
{
public partial class AddApi
{
[Parameter]
public string ApiId { get; set; }
[Inject]
protected IApis_Repositories _apis_Repositories { get; set; }
[Inject]
protected NavigationManager NavigationManager { get; set; }
[Inject]
protected MessageService? Message { get; set; }
private Apis _apiModel = new Apis() ;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (!string.IsNullOrEmpty(ApiId))
{
//查看
_apiModel = _apis_Repositories.GetFirst(p => p.Id == ApiId);
}
}
private void HandleSubmit()
{
if (string.IsNullOrEmpty(ApiId))
{
//新增
_apiModel.Id = Guid.NewGuid().ToString();
if (_apis_Repositories.IsAny(p => p.Name == _apiModel.Name))
{
_ = Message.Error("名称已存在!", 2);
return;
}
string pattern = @"^[A-Za-z]\w*$"; // 正则表达式模式
if (!Regex.IsMatch(_apiModel.Name, pattern))
{
_ = Message.Error("API名称只能是字母、数字、下划线组成且不能以数字开头", 2);
return;
}
_apis_Repositories.Insert(_apiModel);
}
else {
//修改
_apis_Repositories.Update(_apiModel);
}
Back();
}
private void Back() {
NavigationManager.NavigateTo("/plugins/apilist");
}
}
}

View File

@@ -0,0 +1,73 @@
@namespace AntSK.Pages.ApiPage
@using AntSK.Domain.Repositories
@using AntSK.Domain.Model
@page "/plugins/apilist"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="API列表">
<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="Apis"
DataSource="_data"
ItemLayout="ListItemLayout.Horizontal"
Grid="LayoutModel._listGridType">
<ListItem NoFlex>
@if (string.IsNullOrEmpty(context.Id))
{
<Button Type="dashed" class="newButton" @onclick="NavigateToAddApp">
<Icon Type="plus" Theme="outline" /> 创建API
</Button>
}
else
{
<Card Hoverable Bordered Class="card" Actions="@(new[] {
info(()=> Info(context.Id)) ,
delete(async ()=>await Delete(context.Id)) ,
})" Style="max-height:247px;">
<CardMeta>
<AvatarTemplate>
</AvatarTemplate>
<TitleTemplate>
<a>@context.Name</a>
</TitleTemplate>
<DescriptionTemplate>
<Paragraph class="item" Ellipsis>
<!--todo: Ellipsis not working-->
@context.Describe
</Paragraph>
@if (context.Method == HttpMethodType.Get)
{
<Tag Color="@PresetColor.Yellow.ToString()">Get</Tag>
}
else if (context.Method == HttpMethodType.Post)
{
<Tag Color="@PresetColor.Green.ToString()">Post</Tag>
}
</DescriptionTemplate>
</CardMeta>
</Card>
}
</ListItem>
</AntList>
</div>
</ChildContent>
</PageContainer>
@code
{
RenderFragment info(Action clickAction) =>@<a key="info" @onclick="@clickAction">查看</a>;
RenderFragment delete(Action clickAction) => @<a key="delete" @onclick="@clickAction">删除</a> ;
}

View File

@@ -0,0 +1,69 @@
using AntDesign;
using Microsoft.AspNetCore.Components;
using AntSK.Domain.Repositories;
using AntSK.Models;
using AntSK.Services;
namespace AntSK.Pages.ApiPage
{
public partial class ApiList
{
private Apis [] _data = { };
[Inject]
protected IApis_Repositories _apis_Repositories { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await InitData("");
}
private async Task InitData(string searchKey)
{
var list = new List<Apis> { new Apis() };
List<Apis> data;
if (string.IsNullOrEmpty(searchKey))
{
data = await _apis_Repositories.GetListAsync();
}
else
{
data = await _apis_Repositories.GetListAsync(p => p.Name.Contains(searchKey));
}
list.AddRange(data);
_data = list.ToArray();
await InvokeAsync(StateHasChanged);
}
private void NavigateToAddApp()
{
NavigationManager.NavigateTo("/plugins/api/add");
}
private async Task Search(string searchKey)
{
await InitData(searchKey);
}
private void Info(string id)
{
NavigationManager.NavigateTo($"/plugins/api/add/{id}");
}
private async Task Delete(string id)
{
var content = "是否确认删除此Api";
var title = "删除";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
await _apis_Repositories.DeleteAsync(id);
await InitData("");
}
}
}
}

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

@@ -3,6 +3,8 @@
@using AntSK.Models
@page "/App/Add"
@page "/App/Add/{AppId}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="新增应用">
<ChildContent>
@@ -10,38 +12,60 @@
<Form Model="@_appModel"
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="知识库名称" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="知识库名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入知识库名称" @bind-Value="@context.Name" />
</FormItem>
<FormItem Label="图标" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="图标" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入图标" @bind-Value="@context.Icon" />
<a href="https://antblazor.com/zh-CN/components/icon" target="_blank">图标库</a>
</FormItem>
<FormItem Label="类型" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<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="@("chat")">会话应用</Radio>
<Radio RadioButton Value="@("kms")">知识库</Radio>
</RadioGroup>
</FormItem>
<FormItem Label="描述" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
</FormItem>
@if (@context.Type == "chat")
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
</FormItem>
@if (@context.Type == "chat")
{
<FormItem Label="提示词" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea MinRows="4" Placeholder="请输入提示词,用户输入使用{{$input}} 来做占位符" @bind-Value="@context.Prompt" />
</FormItem>
</FormItem>
<FormItem Label="温度系数" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<span>更确定</span>
<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">
<Select Mode="multiple"
@bind-Values="apiIds"
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" />
}
</SelectOptions>
</Select>
</FormItem>
}
@if (@context.Type == "kms")
{
<FormItem Label="知识库" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="知识库" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select Mode="multiple"
@bind-Values="kmsIds"
Placeholder="选择知识库"
TItemValue="string"
TItem="string"
Size="@AntSizeLDSType.Default"
>
Size="@AntSizeLDSType.Default">
<SelectOptions>
@foreach (var kms in _kmsList)
{
@@ -51,21 +75,14 @@
</Select>
</FormItem>
}
<FormItem Label=" " Style="margin-top:32px" WrapperCol="_submitFormLayout.WrapperCol">
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" HtmlType="submit">
保存
</Button>
<Button OnClick="Back">
<Button OnClick="Back">
返回
</Button>
</FormItem>
@if (!string.IsNullOrEmpty(_errorMsg))
{
<Alert Type="@AlertType.Error"
Message="错误"
Description="@_errorMsg"
ShowIcon="true" />
}
</Form>
</Card>
</ChildContent>

View File

@@ -16,8 +16,13 @@ namespace AntSK.Pages.AppPage
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
protected IApis_Repositories _apis_Repositories { get; set; }
[Inject]
protected NavigationManager NavigationManager { get; set; }
[Inject]
protected MessageService? Message { get; set; }
private Apps _appModel = new Apps() ;
@@ -25,42 +30,25 @@ namespace AntSK.Pages.AppPage
private List<Kmss> _kmsList = new List<Kmss>();
IEnumerable<string> apiIds;
private string _errorMsg { get; set; }
private List<Apis> _apiList = new List<Apis>();
private readonly FormItemLayout _formItemLayout = new FormItemLayout
{
LabelCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 7 },
},
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 12 },
Md = new EmbeddedProperty { Span = 10 },
}
};
private readonly FormItemLayout _submitFormLayout = new FormItemLayout
{
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
}
};
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_kmsList = _kmss_Repositories.GetList();
_apiList= _apis_Repositories.GetList();
if (!string.IsNullOrEmpty(AppId))
{
//查看
_appModel= _apps_Repositories.GetFirst(p => p.Id == AppId);
kmsIds = _appModel.KmsIdList?.Split(",");
apiIds= _appModel.ApiFunctionList?.Split(",");
}
}
private void HandleSubmit()
{
@@ -68,9 +56,11 @@ namespace AntSK.Pages.AppPage
{
//新增
_appModel.Id = Guid.NewGuid().ToString();
//秘钥
_appModel.SecretKey="sk-"+ Guid.NewGuid().ToString();
if (_apps_Repositories.IsAny(p => p.Name == _appModel.Name))
{
_errorMsg = "名称已存在!";
_ = Message.Error("名称已存在!", 2);
return;
}
@@ -78,7 +68,10 @@ namespace AntSK.Pages.AppPage
{
_appModel.KmsIdList = string.Join(",", kmsIds);
}
if (apiIds != null && apiIds.Count() > 0)
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
_apps_Repositories.Insert(_appModel);
}
else {
@@ -87,7 +80,10 @@ namespace AntSK.Pages.AppPage
{
_appModel.KmsIdList = string.Join(",", kmsIds);
}
if (apiIds != null && apiIds.Count() > 0)
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
_apps_Repositories.Update(_appModel);
}

View File

@@ -1,11 +0,0 @@
@namespace AntSK.Pages.AppPage
@using AntSK.Domain.Repositories
@using System.ComponentModel.DataAnnotations
@page "/App/Detail/{AppID}"
@inject NavigationManager NavigationManager
<h3>AppDetail</h3>
@code {
}

View File

@@ -1,10 +0,0 @@
using Microsoft.AspNetCore.Components;
namespace AntSK.Pages.AppPage
{
public partial class AppDetail
{
[Parameter]
public string AppId { get; set; }
}
}

View File

@@ -1,7 +1,9 @@
@namespace AntSK.Pages
@namespace AntSK.Pages.AppPage
@using AntSK.Domain.Repositories
@page "/AppList"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="应用列表">
<Content>
@@ -18,7 +20,7 @@
<AntList TItem="Apps"
DataSource="_data"
ItemLayout="ListItemLayout.Horizontal"
Grid="_listGridType">
Grid="LayoutModel._listGridType">
<ListItem NoFlex>
@if (string.IsNullOrEmpty(context.Id))
{
@@ -30,7 +32,9 @@
{
<Card Hoverable Bordered Class="card" Actions="@(new[] {
info(()=> Info(context.Id)) ,
open(()=> Open(context.Id)),
delete(async ()=>await Delete(context.Id)) ,
})" Style="max-height:247px;">
<CardMeta>
<AvatarTemplate>
@@ -44,6 +48,15 @@
<!--todo: Ellipsis not working-->
@context.Describe
</Paragraph>
@if (context.Type == "chat")
{
<Tag Color="@PresetColor.Yellow.ToString()">会话应用</Tag>
}
else if (context.Type == "kms")
{
<Tag Color="@PresetColor.Green.ToString()">知识库</Tag>
}
</DescriptionTemplate>
</CardMeta>
</Card>
@@ -57,5 +70,6 @@
@code
{
RenderFragment info(Action clickAction) =>@<a key="info" @onclick="@clickAction">查看</a>;
RenderFragment open(Action clickAction) =>@<a key="info" @onclick="@clickAction">外部使用</a>;
RenderFragment delete(Action clickAction) => @<a key="delete" @onclick="@clickAction">删除</a> ;
}

View File

@@ -4,21 +4,10 @@ using AntSK.Domain.Repositories;
using AntSK.Models;
using AntSK.Services;
namespace AntSK.Pages
namespace AntSK.Pages.AppPage
{
public partial class AppList
{
private readonly ListGridType _listGridType = new ListGridType
{
Gutter = 16,
Xs = 1,
Sm = 2,
Md = 3,
Lg = 3,
Xl = 4,
Xxl = 4
};
private Apps [] _data = { };
[Inject]
@@ -65,6 +54,10 @@ namespace AntSK.Pages
NavigationManager.NavigateTo($"/app/add/{id}");
}
private void Open(string id)
{
NavigationManager.NavigateTo($"/app/open/{id}");
}
private async Task Delete(string id)

View File

@@ -0,0 +1,60 @@
@namespace AntSK.Pages.AppPage
@page "/App/Open/{AppID}"
<AntDesign.Col Lg="24" Md="24">
<Card Class="tabsCard">
<CardTabs>
<Tabs DefaultActiveKey="1">
<TabPane Key="1">
<TabTemplate>Api接入</TabTemplate>
<ChildContent>
<Card>
<Form Model="@_appModel"
Style="margin-top: 8px;">
<FormItem Label="接口地址" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<Input @bind-Value="@_openApiUrl" ReadOnly="true" />
</FormItem>
<FormItem Label="秘钥" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<Input @bind-Value="@context.SecretKey" ReadOnly="true" />
<Button OnClick="Reset" Style="margin-top:5px;">
重置
</Button>
</FormItem>
<FormItem Label="使用方法" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<TextArea @bind-Value="@_desc" ReadOnly="true" Rows="15" />
</FormItem>
</Form>
</Card>
</ChildContent>
</TabPane>
<TabPane Key="2">
<TabTemplate>页面嵌入</TabTemplate>
<ChildContent>
<Form Model="@_appModel"
Style="margin-top: 8px;">
<FormItem Label="页面地址" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<Input @bind-Value="@_openChatUrl" ReadOnly="true" />
<a href="@_openChatUrl" target="_blank">打开</a>
</FormItem>
<FormItem Label="嵌入脚本" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<TextArea @bind-Value="@_script" ReadOnly="true" Rows="5" />
<span>
将上述代码添加到站点的 body 中<br/>
data-width宽度默认 30rem<br />
data-height高度默认 50rem<br />
data-message-icon-url:嵌入消息图标地址,默认不设置<br />
data-color消息图标颜色默认 #4e83fd<br />
</span>
</FormItem>
</Form>
</ChildContent>
</TabPane>
</Tabs>
</CardTabs>
</Card>
</AntDesign.Col>
@code {
}

View File

@@ -0,0 +1,90 @@
using AntDesign;
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Models;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.AspNetCore.Components;
using Newtonsoft.Json;
namespace AntSK.Pages.AppPage
{
public partial class AppOpen
{
[Parameter]
public string AppId { get; set; }
[Inject]
protected IApps_Repositories _apps_Repositories { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
[Inject]
NavigationManager NavigationManager { get; set; }
private readonly FormItemLayout _formItemLayout = new FormItemLayout
{
LabelCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 7 },
},
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 12 },
Md = new EmbeddedProperty { Span = 10 },
}
};
private Apps _appModel = new Apps();
private string _openApiUrl { get; set; }
private string _openChatUrl { get; set; }
private string _desc { get; set; }
private string _script { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_appModel = _apps_Repositories.GetFirst(p => p.Id == AppId);
_openApiUrl = NavigationManager.BaseUri + "api/v1/chat/completions";
_openChatUrl= NavigationManager.BaseUri + "openchat/"+AppId;
GetDesc();
GetScript();
}
private void GetDesc()
{
_desc = @$"为了方便其他应用对接接口符合openai规范省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "" } } }, Formatting.Indented)}";
}
private void GetScript()
{
_script = $"<script src=\"{NavigationManager.BaseUri}js/iframe.js\" data-width=\"40rem\" data-height=\"80vh\" id=\"antsk-iframe\" data-src=\"{NavigationManager.BaseUri}openchat/{AppId}\" data-color=\"#4e83fd\" data-message-icon-url=\"{NavigationManager.BaseUri}assets/ai.png\"></script>";
}
private void HandleSubmit()
{
}
private async Task Reset()
{
var content = "是否确认重置秘钥重之后之前的秘钥将无法访问API接口";
var title = "重置";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
_appModel.SecretKey = "sk-" + Guid.NewGuid().ToString();
_apps_Repositories.Update(_appModel);
GetDesc();
}
}
}
}

View File

@@ -4,13 +4,15 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization
@page "/Chat"
@page "/Chat/{AppId}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<GridRow Gutter="(16, 16)">
<GridCol Span="12">
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
<Card Style="height:800px;overflow: auto;">
<TitleTemplate>
<Icon Type="setting" /> 选择应用
<GridRow Gutter="(16, 16)">
<GridCol Span="12">
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
<Card Style="height:700px;overflow: auto;">
<TitleTemplate>
<Icon Type="setting" /> 选择应用
<Select DataSource="@_list"
@bind-Value="@AppId"
DefaultValue="@("lucy")"
@@ -18,67 +20,133 @@
LabelProperty="c=>c.Name"
Style="width:200px">
</Select>
</TitleTemplate>
<Body>
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
<GridRow Gutter="(8, 8)">
<Virtualize Items="@(MessageList.OrderByDescending(o => o.CreateTime).ToList())" Context="item">
<GridCol Span="24">
<Card Size="small">
<TitleTemplate>
<Text Strong><Icon Type="bulb" /> @(item.Questions)</Text>
</TitleTemplate>
<Extra>
<Space>
<SpaceItem>
<a style="color: gray;" @onclick="@(() => OnCopyAsync(item))"><Icon Type="copy" /></a>
</SpaceItem>
<SpaceItem>
<a style="color: gray;" @onclick="@(() => OnClearAsync(item.ID))"><Icon Type="rest" /></a>
</SpaceItem>
</Space>
</Extra>
<Body>
@((MarkupString)(item.HtmlAnswers))
</Body>
</Card>
</GridCol>
</Virtualize>
</GridRow>
</div>
<div style="height: 10px;"></div>
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())">
<Suffix>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())"></Button>
</Suffix>
</AntDesign.Input>
</Body>
</Card>
</Spin>
</GridCol>
<GridCol Span="12">
<Card Style="height: 800px;overflow: auto;">
<TitleTemplate>
<Icon Type="search" /> 调试结果
</TitleTemplate>
<Extra>
</Extra>
<Body>
<AntList Bordered DataSource="@RelevantSources">
<ChildContent Context="item">
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<ListItem>
@item.Text
</ListItem>
</ChildContent>
</AntList>
<div id="scrollDiv" style="height: 530px; 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)
{
<GridRow Style="width:100%">
<GridCol Span="23">
<div class="chat-bubble sent">
@(item.Context)
@* <span class="timestamp">@item.CreateTime</span> *@
</div>
</GridCol>
<GridCol Span="1">
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
</GridCol>
</GridRow>
}
else
{
<GridRow Style="width:100%">
<GridCol Span="1">
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" />
</GridCol>
<GridCol Span="23">
<div class="chat-bubble received">
@((MarkupString)(item.HtmlAnswers))
@* <span class="timestamp">@item.CreateTime</span> *@
</div>
</GridCol>
</GridRow>
}
</Virtualize>
</GridRow>
</div>
<div style="height: 10px;"></div>
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
<Suffix>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
</Suffix>
</AntDesign.Input>
</Body>
</Card>
</Spin>
</GridCol>
<GridCol Span="12">
<Card Style="height: 700px;overflow: auto;">
<TitleTemplate>
<Icon Type="search" /> 调试结果
</TitleTemplate>
<Extra>
</Extra>
<Body>
<AntList Bordered DataSource="@RelevantSources" Style="padding:10px;">
<ChildContent Context="item" >
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<Body>
@((MarkupString)(@item.Text))
</Body>
</ChildContent>
</AntList>
</Body>
</Card>
</GridCol>
</GridRow>
<style>
@code {
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 10px;
justify-content: center;
align-items: flex-start;
height: 100vh;
}
.chat-container {
width: 350px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #fff;
padding-bottom: 15px;
}
.chat-bubble {
padding: 10px;
margin: 10px;
margin-bottom: 0;
border-radius: 5px;
max-width: 70%;
position: relative;
}
.received {
background-color: #f0f0f0;
align-self: flex-start;
float: left;
}
.sent {
background-color: #daf8cb;
align-self: flex-end;
float: right;
}
.timestamp {
display: block;
font-size: 0.75em;
margin-top: 5px;
}
.received .timestamp {
text-align: right;
margin-right: 10px;
}
.sent .timestamp {
text-align: left;
margin-left: 10px;
}
</style>
@code {
}

View File

@@ -1,18 +1,24 @@
using AntDesign;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Azure.AI.OpenAI;
using Azure.Core;
using DocumentFormat.OpenXml.EMMA;
using DocumentFormat.OpenXml.Wordprocessing;
using MarkdownSharp;
using Microsoft.AspNetCore.Components;
using Microsoft.KernelMemory;
using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Newtonsoft.Json;
using RestSharp;
using SqlSugar;
using System;
using System.Text;
namespace AntSK.Pages.ChatPage
{
@@ -20,23 +26,28 @@ namespace AntSK.Pages.ChatPage
{
[Parameter]
public string AppId { get; set; }
[Inject]
[Inject]
protected MessageService? Message { get; set; }
[Inject]
protected IApps_Repositories _apps_Repositories { get; set; }
[Inject]
protected IApis_Repositories _apis_Repositories { get; set; }
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject]
protected MemoryServerless _memory { get; set; }
//[Inject]
//protected Kernel _kernel { get; set; }
[Inject]
protected Kernel _kernel { get; set; }
protected IKernelService _kernelService { get; set; }
protected bool _loading = false;
protected List<MessageInfo> MessageList = [];
protected string? _messageInput;
protected string _json = "";
protected bool Sendding = false;
List<RelevantSource> RelevantSources = new List<RelevantSource>();
@@ -44,31 +55,49 @@ namespace AntSK.Pages.ChatPage
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_list= _apps_Repositories.GetList();
_list = _apps_Repositories.GetList();
}
protected async Task OnSendAsync()
{
if (string.IsNullOrWhiteSpace(_messageInput))
try
{
_ = Message.Info("请输入消息", 2);
return;
}
if (string.IsNullOrWhiteSpace(_messageInput))
{
_ = Message.Info("请输入消息", 2);
return;
}
if (string.IsNullOrWhiteSpace(AppId))
if (string.IsNullOrWhiteSpace(AppId))
{
_ = Message.Info("请选择应用进行测试", 2);
return;
}
MessageList.Add(new MessageInfo()
{
ID = Guid.NewGuid().ToString(),
Context = _messageInput,
CreateTime = DateTime.Now,
IsSend = true
});
Sendding = true;
await SendAsync(_messageInput);
_messageInput = "";
Sendding = false;
}
catch (System.Exception ex)
{
_ = Message.Info("请选择应用进行测试", 2);
return;
Sendding = false;
_ = Message.Error("异常:"+ex.Message, 2);
}
await SendAsync(_messageInput);
_messageInput = "";
}
protected async Task OnCopyAsync(MessageInfo item)
{
await Task.Run(() =>
{
_messageInput = item.Questions;
_messageInput = item.Context;
});
}
@@ -82,82 +111,199 @@ namespace AntSK.Pages.ChatPage
protected async Task<bool> SendAsync(string questions)
{
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
string msg = "";
//处理多轮会话
if (MessageList.Count > 0)
{
msg = await HistorySummarize(questions);
}
Apps app = _apps_Repositories.GetFirst(p => p.Id == AppId);
switch (app.Type)
{
case "chat":
//普通会话
var promptTemplateFactory = new KernelPromptTemplateFactory();
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
var renderedPrompt = await promptTemplate.RenderAsync(_kernel);
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, new OpenAIPromptExecutionSettings() );
var chatResult = await _kernel.InvokeAsync(func,new KernelArguments() { ["input"]=questions});
if (chatResult.IsNotNull())
{
string answers = chatResult.GetValue<string>();
var markdown = new Markdown();
string htmlAnswers = markdown.Transform(answers);
var info = new MessageInfo()
{
ID = Guid.NewGuid().ToString(),
Questions = questions,
Answers = answers,
HtmlAnswers = htmlAnswers,
CreateTime = DateTime.Now,
};
MessageList.Add(info);
}
await SendChat(questions, msg, app);
break;
case "kms":
//知识库问答
var filters = new List<MemoryFilter>();
var kmsidList = app.KmsIdList.Split(",");
foreach (var kmsid in kmsidList)
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var kmsResult = await _memory.AskAsync(questions, index: "kms", filters: filters);
if (kmsResult != null)
{
if (!string.IsNullOrEmpty(kmsResult.Result))
{
string answers = kmsResult.Result;
var markdown = new Markdown();
string htmlAnswers = markdown.Transform(answers);
var info = new MessageInfo()
{
ID = Guid.NewGuid().ToString(),
Questions = questions,
Answers = answers,
HtmlAnswers = htmlAnswers,
CreateTime = DateTime.Now,
};
MessageList.Add(info);
}
foreach (var x in kmsResult.RelevantSources)
{
foreach (var xsd in x.Partitions)
{
string sourceName = x.SourceName;
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == x.SourceName);
if (fileDetail.IsNotNull())
{
sourceName = fileDetail.FileName;
}
RelevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = xsd.Text, Relevance = xsd.Relevance });
}
}
}
await SendKms(questions, msg, app);
break;
}
return await Task.FromResult(true);
}
/// <summary>
/// 发送知识库问答
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendKms(string questions, string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
//知识库问答
var filters = new List<MemoryFilter>();
var kmsidList = app.KmsIdList.Split(",");
foreach (var kmsid in kmsidList)
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
string dataMsg = "";
if (xlresult != null)
{
foreach (var item in xlresult.Results)
{
foreach (var part in item.Partitions)
{
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance*100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
//输出调试信息
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 });
}
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: jsonFun,
arguments: new KernelArguments() { ["data"] = dataMsg, ["history"] = msg, ["questions"]=questions });
MessageInfo info = null;
var markdown1 = new Markdown();
await foreach (var content in chatResult)
{
if (info == null)
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info.Context = content?.Content?.ConvertToString();
info.HtmlAnswers = content?.Content?.ConvertToString();
info.CreateTime = DateTime.Now;
MessageList.Add(info);
}
else
{
info.HtmlAnswers += content.Content;
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
if (info.IsNotNull())
{
info!.HtmlAnswers = markdown1.Transform(info.HtmlAnswers);
}
await InvokeAsync(StateHasChanged);
}
}
/// <summary>
/// 发送普通对话
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendChat(string questions, string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
if (string.IsNullOrEmpty(app.Prompt)||!app.Prompt.Contains("{{$input}}"))
{
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString()+"{{$input}}";
}
var temperature = app.Temperature/100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() {Temperature= temperature };
if (!string.IsNullOrEmpty(app.ApiFunctionList))
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,Temperature = temperature };
}
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
MessageInfo info = null;
var markdown = new Markdown();
await foreach (var content in chatResult)
{
if (info == null)
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info.Context = content?.Content?.ConvertToString();
info.HtmlAnswers = content?.Content?.ConvertToString();
info.CreateTime = DateTime.Now;
MessageList.Add(info);
}
else
{
info.HtmlAnswers += content.Content;
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
if (info.IsNotNull())
{
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
}
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// 历史会话的会话总结
/// </summary>
/// <param name="questions"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(string questions)
{
var _kernel = _kernelService.GetKernel();
if (MessageList.Count > 1)
{
StringBuilder history = new StringBuilder();
foreach (var item in MessageList)
{
if (item.IsSend)
{
history.Append($"user:{item.Context}{Environment.NewLine}");
}
else
{
history.Append($"assistant:{item.Context}{Environment.NewLine}");
}
}
if (MessageList.Count > 10)
{
//历史会话大于10条进行总结
var msg = await _kernelService.HistorySummarize(_kernel, questions, history.ToString());
return msg;
}
else
{
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}"; ;
return msg;
}
}
else
{
return questions;
}
}
}
public class RelevantSource

View File

@@ -0,0 +1,114 @@
@namespace AntSK.Pages.ChatPage
@using AntSK.Domain.Repositories
@using AntSK.Models
@using Microsoft.AspNetCore.Components.Web.Virtualization
@page "/OpenChat/{AppId}"
@layout OpenLayout
<div id="chat" style="display:flex; flex-direction:column; height:100%; overflow-x:hidden;">
<PageHeader Class="site-page-header" Title="@app.Name" Subtitle="@app.Describe" />
<div id="scrollDiv" style="flex:1; width:100%; overflow-y:auto; overflow-x:hidden;padding:10px;">
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
@if (item.IsSend)
{
<GridRow>
<GridCol Span="23">
<div class="chat-bubble sent">
@(item.Context)
@* <span class="timestamp">@item.CreateTime</span> *@
</div>
</GridCol>
<GridCol Span="1">
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
</GridCol>
</GridRow>
}
else
{
<GridRow>
<GridCol Span="1">
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" />
</GridCol>
<GridCol Span="23">
<div class="chat-bubble received">
@((MarkupString)(item.HtmlAnswers))
@* <span class="timestamp">@item.CreateTime</span> *@
</div>
</GridCol>
</GridRow>
}
</Virtualize>
</div>
<div style="flex-shrink:0;margin:10px;">
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
<Suffix>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
</Suffix>
</AntDesign.Input>
</div>
</div>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 10px;
justify-content: center;
align-items: flex-start;
height: 100vh;
}
.chat-container {
width: 350px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #fff;
padding-bottom: 15px;
}
.chat-bubble {
padding: 10px;
margin: 10px;
margin-bottom: 0;
border-radius: 5px;
max-width: 70%;
position: relative;
}
.received {
background-color: #f0f0f0;
align-self: flex-start;
float: left;
}
.sent {
background-color: #daf8cb;
align-self: flex-end;
float: right;
}
.timestamp {
display: block;
font-size: 0.75em;
margin-top: 5px;
}
.received .timestamp {
text-align: right;
margin-right: 10px;
}
.sent .timestamp {
text-align: left;
margin-left: 10px;
}
</style>
@code {
}

View File

@@ -0,0 +1,285 @@
using AntDesign;
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Azure.AI.OpenAI;
using Azure.Core;
using DocumentFormat.OpenXml.EMMA;
using MarkdownSharp;
using Microsoft.AspNetCore.Components;
using Microsoft.KernelMemory;
using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Text;
using AntSK.Domain.Utils;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Service;
namespace AntSK.Pages.ChatPage
{
public partial class OpenChat
{
[Parameter]
public string AppId { get; set; }
[Inject]
protected MessageService? Message { get; set; }
[Inject]
protected IApps_Repositories _apps_Repositories { get; set; }
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject]
protected MemoryServerless _memory { get; set; }
[Inject]
protected IKernelService _kernelService { get; set; }
protected bool _loading = false;
protected List<MessageInfo> MessageList = [];
protected string? _messageInput;
protected string _json = "";
protected bool Sendding = false;
protected Apps app = new Apps();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
app = _apps_Repositories.GetFirst(p=>p.Id==AppId);
}
protected async Task OnSendAsync()
{
try
{
if (string.IsNullOrWhiteSpace(_messageInput))
{
_ = Message.Info("请输入消息", 2);
return;
}
MessageList.Add(new MessageInfo()
{
ID = Guid.NewGuid().ToString(),
Context = _messageInput,
CreateTime = DateTime.Now,
IsSend = true
});
Sendding = true;
await SendAsync(_messageInput);
_messageInput = "";
Sendding = false;
}
catch (System.Exception ex)
{
Sendding = false;
_ = Message.Error("异常:"+ex.Message, 2);
}
}
protected async Task OnCopyAsync(MessageInfo item)
{
await Task.Run(() =>
{
_messageInput = item.Context;
});
}
protected async Task OnClearAsync(string id)
{
await Task.Run(() =>
{
MessageList = MessageList.Where(w => w.ID != id).ToList();
});
}
protected async Task<bool> SendAsync(string questions)
{
string msg = "";
//处理多轮会话
if (MessageList.Count > 0)
{
msg = await HistorySummarize(questions);
}
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
switch (app.Type)
{
case "chat":
//普通会话
await SendChat(questions, msg, app);
break;
case "kms":
//知识库问答
await SendKms(questions, msg, app);
break;
}
return await Task.FromResult(true);
}
/// <summary>
/// 发送知识库问答
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendKms(string questions, string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
//知识库问答
var filters = new List<MemoryFilter>();
var kmsidList = app.KmsIdList.Split(",");
foreach (var kmsid in kmsidList)
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
string dataMsg = "";
if (xlresult != null)
{
foreach (var item in xlresult.Results)
{
foreach (var part in item.Partitions)
{
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance * 100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
}
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: jsonFun,
arguments: new KernelArguments() { ["data"] = dataMsg, ["history"] = msg, ["questions"] = questions });
MessageInfo info = null;
var markdown1 = new Markdown();
await foreach (var content in chatResult)
{
if (info == null)
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info.Context = content?.Content?.ConvertToString();
info.HtmlAnswers = content?.Content?.ConvertToString();
info.CreateTime = DateTime.Now;
MessageList.Add(info);
}
else
{
info.HtmlAnswers += content.Content;
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
if (info.IsNotNull())
{
info!.HtmlAnswers = markdown1.Transform(info.HtmlAnswers);
}
await InvokeAsync(StateHasChanged);
}
}
/// <summary>
/// 发送普通对话
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendChat(string questions, string msg, Apps app)
{
var _kernel= _kernelService.GetKernel();
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
{
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
}
//注册插件
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
if (!string.IsNullOrEmpty(app.ApiFunctionList))
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, Temperature = temperature };
}
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
MessageInfo info = null;
var markdown = new Markdown();
await foreach (var content in chatResult)
{
if (info == null)
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info.Context = content?.Content?.ConvertToString();
info.HtmlAnswers = content?.Content?.ConvertToString();
info.CreateTime = DateTime.Now;
MessageList.Add(info);
}
else
{
info.HtmlAnswers += content.Content;
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// 历史会话的会话总结
/// </summary>
/// <param name="questions"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(string questions)
{
var _kernel = _kernelService.GetKernel();
if (MessageList.Count > 1)
{
StringBuilder history = new StringBuilder();
foreach (var item in MessageList)
{
if (item.IsSend)
{
history.Append($"user:{item.Context}{Environment.NewLine}");
}
else
{
history.Append($"assistant:{item.Context}{Environment.NewLine}");
}
}
if (MessageList.Count > 10)
{
//历史会话大于10条进行总结
var msg = await _kernelService.HistorySummarize(_kernel, questions, history.ToString());
return msg;
}
else
{
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}"; ;
return msg;
}
}
else
{
return questions;
}
}
}
}

View File

@@ -0,0 +1,3 @@
.ant-card-body {
height: 90% !important;
}

View File

@@ -1,10 +1,27 @@
@namespace AntSK.Pages.Exception
@page "/exception/404"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<Result Status="404"
Title="404"
SubTitle="Sorry, the page you visited does not exist.">
<Extra>
<Button Type="primary">Back Home</Button>
</Extra>
</Result>
<div class="rail">
@for (var i = 0; i < StampCount; i++)
{
<div class="@((i % 2 == 0) ? "stamp zero" : "stamp four")">
@(i % 2 == 0 ? "4" : "0")
</div>
}
<div class="world">
<div class="forward">
<div class="box">
@for (var i = 0; i < 6; i++)
{
<div class="wall"></div>
}
</div>
</div>
</div>
</div>
@code {
private int StampCount { get; set; } = 20; // 初始的循环次数
}

View File

@@ -0,0 +1,245 @@
body {
background: #fff;
height: 100vh;
overflow: hidden;
display: flex;
font-family: "Anton", sans-serif;
justify-content: center;
align-items: center;
perspective: 1000px;
}
div {
transform-style: preserve-3d;
}
.rail {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
transform: rotateX(-30deg) rotateY(-50deg);
}
.rail .stamp {
position: absolute;
width: 200px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
background: rgb(20, 20, 20);
color: #fff;
font-size: 7rem;
}
.rail .stamp:nth-child(1) {
animation: stampSlide 40000ms -2300ms linear infinite;
}
.rail .stamp:nth-child(2) {
animation: stampSlide 40000ms -4300ms linear infinite;
}
.rail .stamp:nth-child(3) {
animation: stampSlide 40000ms -6300ms linear infinite;
}
.rail .stamp:nth-child(4) {
animation: stampSlide 40000ms -8300ms linear infinite;
}
.rail .stamp:nth-child(5) {
animation: stampSlide 40000ms -10300ms linear infinite;
}
.rail .stamp:nth-child(6) {
animation: stampSlide 40000ms -12300ms linear infinite;
}
.rail .stamp:nth-child(7) {
animation: stampSlide 40000ms -14300ms linear infinite;
}
.rail .stamp:nth-child(8) {
animation: stampSlide 40000ms -16300ms linear infinite;
}
.rail .stamp:nth-child(9) {
animation: stampSlide 40000ms -18300ms linear infinite;
}
.rail .stamp:nth-child(10) {
animation: stampSlide 40000ms -20300ms linear infinite;
}
.rail .stamp:nth-child(11) {
animation: stampSlide 40000ms -22300ms linear infinite;
}
.rail .stamp:nth-child(12) {
animation: stampSlide 40000ms -24300ms linear infinite;
}
.rail .stamp:nth-child(13) {
animation: stampSlide 40000ms -26300ms linear infinite;
}
.rail .stamp:nth-child(14) {
animation: stampSlide 40000ms -28300ms linear infinite;
}
.rail .stamp:nth-child(15) {
animation: stampSlide 40000ms -30300ms linear infinite;
}
.rail .stamp:nth-child(16) {
animation: stampSlide 40000ms -32300ms linear infinite;
}
.rail .stamp:nth-child(17) {
animation: stampSlide 40000ms -34300ms linear infinite;
}
.rail .stamp:nth-child(18) {
animation: stampSlide 40000ms -36300ms linear infinite;
}
.rail .stamp:nth-child(19) {
animation: stampSlide 40000ms -38300ms linear infinite;
}
.rail .stamp:nth-child(20) {
animation: stampSlide 40000ms -40300ms linear infinite;
}
@keyframes stampSlide {
0% {
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
}
100% {
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
}
}
.world .forward {
position: absolute;
animation: slide 2000ms linear infinite;
}
.world .box {
width: 200px;
height: 200px;
transform-origin: 100% 100%;
animation: roll 2000ms cubic-bezier(1, 0.01, 1, 1) infinite;
}
.world .box .wall {
position: absolute;
width: 200px;
height: 200px;
background: rgba(10, 10, 10, 0.8);
border: 1px solid rgb(250, 250, 250);
box-sizing: border-box;
}
.world .box .wall::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 7rem;
}
.world .box .wall:nth-child(1) {
transform: translateZ(100px);
}
.world .box .wall:nth-child(2) {
transform: rotateX(180deg) translateZ(100px);
}
.world .box .wall:nth-child(3) {
transform: rotateX(90deg) translateZ(100px);
}
.world .box .wall:nth-child(3)::before {
transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
animation: zeroFour 4000ms -2000ms linear infinite;
}
.world .box .wall:nth-child(4) {
transform: rotateX(-90deg) translateZ(100px);
}
.world .box .wall:nth-child(4)::before {
transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
animation: zeroFour 4000ms -2000ms linear infinite;
}
.world .box .wall:nth-child(5) {
transform: rotateY(90deg) translateZ(100px);
}
.world .box .wall:nth-child(5)::before {
transform: rotateX(180deg) translateZ(-1px);
animation: zeroFour 4000ms linear infinite;
}
.world .box .wall:nth-child(6) {
transform: rotateY(-90deg) translateZ(100px);
}
.world .box .wall:nth-child(6)::before {
transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
animation: zeroFour 4000ms linear infinite;
}
@keyframes zeroFour {
0% {
content: "4";
}
100% {
content: "0";
}
}
@keyframes roll {
0% {
transform: rotateZ(0deg);
}
85% {
transform: rotateZ(90deg);
}
87% {
transform: rotateZ(88deg);
}
90% {
transform: rotateZ(90deg);
}
100% {
transform: rotateZ(90deg);
}
}
@keyframes slide {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-200px);
}
}

41
AntSK/Pages/Index.razor Normal file
View File

@@ -0,0 +1,41 @@
@namespace AntSK.Pages
@using MarkdownSharp
@page "/"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<Body>
@((MarkupString)(body))
<Image Width="200px" Src="@imgSrc" />
</Body>
@code {
[Inject]
NavigationManager NavigationManager { get; set; }
private string imgSrc { get; set; }
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

@@ -3,6 +3,8 @@
@using AntSK.Models
@page "/Kms/Add"
@inject IMessageService _message
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="新增知识库">
<ChildContent>
@@ -11,29 +13,31 @@
Model="@_kmsModel"
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="知识库名称" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="知识库名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入知识库名称" @bind-Value="@context.Name" />
</FormItem>
<FormItem Label="图标" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="图标" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入图标" @bind-Value="@context.Icon" />
<a href="https://antblazor.com/zh-CN/components/icon" target="_blank">图标库</a>
</FormItem>
<FormItem Label="描述" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
</FormItem>
<FormItem Label=" " Style="margin-top:32px" WrapperCol="_submitFormLayout.WrapperCol">
<FormItem Label="段落切片数(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<AntDesign.InputNumber @bind-Value="context.MaxTokensPerParagraph" PlaceHolder="段落切片数"></AntDesign.InputNumber>
</FormItem>
<FormItem Label="行切片数(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<AntDesign.InputNumber @bind-Value="context.MaxTokensPerLine" PlaceHolder="行切片数"></AntDesign.InputNumber>
</FormItem>
<FormItem Label="重叠部分(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<AntDesign.InputNumber @bind-Value="context.OverlappingTokens" PlaceHolder="重叠部分"></AntDesign.InputNumber>
</FormItem>
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" HtmlType="submit">
保存
</Button>
</FormItem>
@if ( !string.IsNullOrEmpty(_errorMsg))
{
<Alert Type="@AlertType.Error"
Message="错误"
Description="@_errorMsg"
ShowIcon="true"
/>
}
</Form>
</Card>
</ChildContent>

View File

@@ -13,41 +13,17 @@ namespace AntSK.Pages.KmsPage
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
protected NavigationManager NavigationManager { get; set; }
private string _errorMsg { get; set; }
[Inject]
protected MessageService? Message { get; set; }
private readonly Kmss _kmsModel = new Kmss() ;
private readonly FormItemLayout _formItemLayout = new FormItemLayout
{
LabelCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 7 },
},
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24 },
Sm = new EmbeddedProperty { Span = 12 },
Md = new EmbeddedProperty { Span = 10 },
}
};
private readonly FormItemLayout _submitFormLayout = new FormItemLayout
{
WrapperCol = new ColLayoutParam
{
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
}
};
private void HandleSubmit()
{
_kmsModel.Id = Guid.NewGuid().ToString();
if (_kmss_Repositories.IsAny(p => p.Name == _kmsModel.Name))
{
_errorMsg = "名称已存在!";
_ = Message.Error("名称已存在!", 2);
return;
}

View File

@@ -3,6 +3,8 @@
@using System.ComponentModel.DataAnnotations
@page "/Kms/Detail/{KmsID}"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<div>
<PageContainer Title="知识库文档">
@@ -17,8 +19,9 @@
<Dropdown Style="position: absolute; right: 20px; margin-bottom: 8px;">
<Overlay>
<Menu>
@( _fileUpload( () =>FileShowModal()))
@(_fileUpload(() => FileShowModal()))
@(_urlUpload(() => UrlShowModal()))
@(_textUpload(() => TextShowModal()))
</Menu>
</Overlay>
<ChildContent>
@@ -27,20 +30,20 @@
</Dropdown>
<div class="extraContent" style="margin-right:100px;">
<Search Class="extraContentSearch" Placeholder="搜索文档" @bind-Value="_model.Id" />
</div>
</Extra>
<ChildContent >
<AntList TItem="KmsDetails"
DataSource="_data"
ItemLayout="ListItemLayout.Horizontal">
<ListItem Actions="@(new[] {
</div>
</Extra>
<ChildContent>
<AntList TItem="KmsDetails"
DataSource="_data"
ItemLayout="ListItemLayout.Horizontal">
<ListItem Actions="@(new[] {
detail(()=> FileDetail(context.Id)) ,
delete(async ()=>await DeleteFile(context.Id)) ,
})">
<ListItemMeta Description="@context.Id">
<TitleTemplate>
<div >文件ID</div>
<div>文件ID</div>
</TitleTemplate>
</ListItemMeta>
<ListItemMeta Description="@context.Type">
@@ -48,15 +51,15 @@
<div>文件类型</div>
</TitleTemplate>
</ListItemMeta>
@if(@context.Type=="file")
@if (@context.Type == "file")
{
<ListItemMeta Avatar="" Description="@context.FileName">
<TitleTemplate>
<a >文件名称</a>
</TitleTemplate>
</ListItemMeta>
<ListItemMeta Avatar="" Description="@context.FileName">
<TitleTemplate>
<a>文件名称</a>
</TitleTemplate>
</ListItemMeta>
}
else
else if (@context.Type == "url")
{
<ListItemMeta Avatar="" Description="@context.Url">
<TitleTemplate>
@@ -64,10 +67,17 @@
</TitleTemplate>
</ListItemMeta>
}
else if (@context.Type == "text")
{
<ListItemMeta Avatar="" Description="……">
<TitleTemplate>
<a>文本内容</a>
</TitleTemplate>
</ListItemMeta>
}
<ListItemMeta Avatar="" Description="@context.DataCount.ToString()">
<TitleTemplate>
<a >文档切片数量</a>
<a>文档切片数量</a>
</TitleTemplate>
</ListItemMeta>
<div class="listContent">
@@ -100,6 +110,21 @@
</Form>
</Modal>
<Modal Title="文本导入"
Visible="@_textVisible"
OnOk="@TextHandleOk"
OnCancel="@TextHandleCancel"
ConfirmLoading="@_textConfirmLoading">
<Form Model="@textModel"
LabelColSpan="8"
WrapperColSpan="16"
@ref="@_textForm">
<FormItem Label="文本内容">
<TextArea @bind-Value="@context.Text" Rows="5" />
</FormItem>
</Form>
</Modal>
<Modal Title="文件导入"
Visible="@_fileVisible"
OnOk="@FileHandleOk"
@@ -118,33 +143,40 @@
application/vnd.openxmlformats-officedocument.presentationml.presentation,
application/pdf,
application/json,
text/markdown"
text/markdown,
text/x-markdown"
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">
支持txt、word、pdf、md、excel、ppt等文件。
</p>
</Upload>
<p class="ant-upload-drag-icon">
<Icon Type="inbox" />
</p>
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
<p class="ant-upload-hint">
支持txt、word、pdf、md、excel、ppt等文件。
</p>
</Upload>
</Modal>
@code {
RenderFragment _fileUpload(Action clickAction) =>@<MenuItem>
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
文件导入
</a>
</MenuItem>;
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
文件导入
</a>
</MenuItem>;
RenderFragment _urlUpload(Action clickAction) =>@<MenuItem>
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
链接读取
</a>
</MenuItem>;
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
链接读取
</a>
</MenuItem>;
RenderFragment _textUpload(Action clickAction) =>@<MenuItem>
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
文本导入
</a>
</MenuItem>;
RenderFragment detail(Action clickAction) => @<a key="detail" @onclick="@clickAction">详情</a>;
RenderFragment delete(Action clickAction) => @<a key="edit" @onclick="@clickAction">删除</a>;

View File

@@ -31,12 +31,18 @@ namespace AntSK.Pages.KmsPage
bool _fileVisible = false;
bool _fileConfirmLoading = false;
bool _textVisible = false;
bool _textConfirmLoading = false;
string filePath;
string fileName;
private Form<UrlModel> _urlForm;
private UrlModel urlModel = new UrlModel();
private Form<TextModel> _textForm;
private TextModel textModel = new TextModel();
private readonly IDictionary<string, ProgressStatus> _pStatus = new Dictionary<string, ProgressStatus>
{
{"active", ProgressStatus.Active},
@@ -51,8 +57,11 @@ namespace AntSK.Pages.KmsPage
protected IConfirmService _confirmService { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject]
protected MemoryServerless _memory { get; set; }
protected IKmss_Repositories _kmss_Repositories { get; set; }
private MemoryServerless _memory { get; set; }
[Inject]
protected IKMService iKMService { get; set; }
[Inject]
@@ -63,6 +72,13 @@ namespace AntSK.Pages.KmsPage
{
await base.OnInitializedAsync();
_data =await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
var km = _kmss_Repositories.GetFirst(p => p.Id == KmsId);
//使用知识库设置的参数,
_memory = iKMService.GetMemory(textPartitioningOptions:new Microsoft.KernelMemory.Configuration.TextPartitioningOptions() {
MaxTokensPerLine= km.MaxTokensPerLine,
MaxTokensPerParagraph=km.MaxTokensPerParagraph ,
OverlappingTokens=km.OverlappingTokens
});
}
/// <summary>
/// 根据文档ID获取文档
@@ -117,10 +133,56 @@ namespace AntSK.Pages.KmsPage
}
#endregion
#region Text
public class TextModel
{
[Required]
public string Text { get; set; }
}
private async Task TextHandleOk(MouseEventArgs e)
{
try
{
_textConfirmLoading = true;
string fileid = Guid.NewGuid().ToString();
await _memory.ImportTextAsync(textModel.Text, fileid, new TagCollection() { { "kmsid", KmsId } }
, index: "kms");
//查询文档数量
var docTextList = await iKMService.GetDocumentByFileID(fileid);
KmsDetails detial = new KmsDetails()
{
Id = fileid,
KmsId = KmsId,
Type = "text",
DataCount = docTextList.Count,
CreateTime = DateTime.Now
};
await _kmsDetails_Repositories.InsertAsync(detial);
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
_textVisible = false;
_textConfirmLoading = false;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void TextHandleCancel(MouseEventArgs e)
{
_textVisible = false;
}
private void TextShowModal()
{
_textVisible = true;
}
#endregion
#region File
private async Task FileHandleOk(MouseEventArgs e)
{
try
@@ -177,10 +239,11 @@ namespace AntSK.Pages.KmsPage
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/pdf",
"application/json",
"text/x-markdown",
"text/markdown"
};
var IsType = types.Contains( file.Type );
if (!IsType)
if (!IsType&& file.Ext != ".md")
{
_message.Error("文件格式错误,请重新选择!");
}

View File

@@ -3,6 +3,8 @@
@using AntSK.Domain.Domain.Dto
@page "/Kms/DetailList/{KmsID}/{FileId}"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<Button Type="@ButtonType.Primary" OnClick="NavigateBack">返回</Button>

View File

@@ -2,6 +2,8 @@
@using AntSK.Domain.Repositories
@page "/KmsList"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="知识库列表">
@@ -10,7 +12,7 @@
<Search Placeholder="输入回车"
EnterButton="@("搜索")"
Size="large"
Style="max-width: 522px; width: 100%;"
Style="max-width: 522px; width: 100%;"
OnSearch="Search" />
</div>
</Content>
@@ -24,7 +26,7 @@
@if (string.IsNullOrEmpty(context.Id))
{
<Button Type="dashed" class="newButton" @onclick="NavigateToAddKms">
<Icon Type="plus" Theme="outline" /> 创建应用
<Icon Type="plus" Theme="outline" /> 创建知识库
</Button>
}
else
@@ -43,8 +45,12 @@
<DescriptionTemplate>
<Paragraph class="item" Ellipsis>
<!--todo: Ellipsis not working-->
@context.Describe
</Paragraph>
<span>
@context.Describe
</span>
<br />
<span style="color:#c6c6c6">行切片:@context.MaxTokensPerLine 段落切片:@context.MaxTokensPerParagraph 重叠:@context.OverlappingTokens</span>
</Paragraph>
</DescriptionTemplate>
</CardMeta>
</Card>

View File

@@ -0,0 +1,52 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model.Enum
@page "/setting/model/add"
@page "/setting/model/add/{ModelId}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="新增模型">
<ChildContent>
<Card>
<Form Model="@_aiModel"
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="模型描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型描述" @bind-Value="@context.ModelDescription" />
</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>
<FormItem Label="模型地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型地址" @bind-Value="@context.EndPoint" />
</FormItem>
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
</FormItem>
<FormItem Label="模型秘钥" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<InputPassword @bind-Value="@context.ModelKey" Placeholder="请输入模型秘钥" Size="@InputSize.Large" />
</FormItem>
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" OnClick="HandleSubmit">
保存
</Button>
<Button OnClick="Back">
返回
</Button>
</FormItem>
</Form>
</Card>
</ChildContent>
</PageContainer>
@code {
}

View File

@@ -0,0 +1,68 @@
using AntDesign;
using AntDesign.ProLayout;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using DocumentFormat.OpenXml.InkML;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.AspNetCore.Components;
namespace AntSK.Pages.Setting.AIModel
{
public partial class AddModel
{
[Parameter]
public string ModelId { 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();
IEnumerable<string> _menuKeys;
private List<MenuDataItem> menuList = new List<MenuDataItem>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (!string.IsNullOrEmpty(ModelId))
{
_aiModel= _aimodels_Repositories.GetFirst(p => p.Id == ModelId);
}
}
private void HandleSubmit()
{
if (string.IsNullOrEmpty(ModelId))
{
//新增
_aiModel.Id = Guid.NewGuid().ToString();
if (_aimodels_Repositories.IsAny(p => p.ModelDescription == _aiModel.ModelDescription ))
{
_ = Message.Error("模型描述已存在!", 2);
return;
}
if (_aimodels_Repositories.IsAny(p =>p.AIModelType==_aiModel.AIModelType&& p.EndPoint == _aiModel.EndPoint&&p.ModelKey==_aiModel.ModelKey&&p.ModelName==_aiModel.ModelName))
{
_ = Message.Error("模型已存在!", 2);
return;
}
_aimodels_Repositories.Insert(_aiModel);
}
else
{
_aimodels_Repositories.Update(_aiModel);
}
Back();
}
private void Back()
{
NavigationManager.NavigateTo("/setting/modellist");
}
}
}

View File

@@ -0,0 +1,15 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/modeldown"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="模型下载">
<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>
</ChildContent>
</PageContainer>
@code {
}

View File

@@ -0,0 +1,79 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Domain.Model.Enum
@page "/setting/modellist"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
<div>
<PageContainer Title="模型管理">
<ChildContent>
<div class="standardList">
<Card Class="listCard"
Title="模型列表"
Style="margin-top: 24px;"
BodyStyle="padding: 0 32px 40px 32px">
<Extra>
<div class="extraContent">
<Search Class="extraContentSearch" Placeholder="查询" @bind-Value="_searchKeyword" OnSearch="OnSearch" />
</div>
</Extra>
<ChildContent>
<Button Type="dashed"
Style="width: 100%; margin-bottom: 8px;"
OnClick="AddModel">
<Icon Type="plus" Theme="outline" />
新增模型
</Button>
<AntList TItem="AIModels"
DataSource="_data"
ItemLayout="ListItemLayout.Horizontal">
<ListItem Actions="new[] {
edit(()=> Edit(context.Id)),
delete(async ()=>await Delete(context.Id))
}" Style="width:100%">
<div class="listContent" style="width:100%">
<div class="listContentItem" style="width:20%">
<b>模型描述</b>
<p>@context.ModelDescription</p>
</div>
<div class="listContentItem" style="width:20%">
<b>模型类别</b>
<p>
@if (context.AIModelType == AIModelType.Chat)
{
<Tag Color="@PresetColor.Yellow.ToString()">会话模型</Tag>
}
else if (context.AIModelType == AIModelType.Embedding)
{
<Tag Color="@PresetColor.Green.ToString()">向量模型</Tag>
}
</p>
</div>
<div class="listContentItem" style="width:20%">
<b>模型地址</b>
<p>@context.EndPoint</p>
</div>
<div class="listContentItem" style="width:20%">
<b>模型名称</b>
<p>@context.ModelName</p>
</div>
</div>
</ListItem>
</AntList>
</ChildContent>
</Card>
</div>
</ChildContent>
</PageContainer>
</div>
@code
{
RenderFragment edit(Action clickAction) =>@<a key="edit" @onclick="@clickAction">修改</a>;
RenderFragment delete(Action clickAction) =>@<a key="delete" @onclick="@clickAction">删除</a>;
}

View File

@@ -0,0 +1,67 @@
using AntDesign;
using AntSK.Domain.Repositories;
using AntSK.Models;
using AntSK.Services;
using DocumentFormat.OpenXml.Office2010.Excel;
using Microsoft.AspNetCore.Components;
namespace AntSK.Pages.Setting.AIModel
{
public partial class ModelList
{
private readonly BasicListFormModel _model = new BasicListFormModel();
private List<AIModels> _data;
private string _searchKeyword;
[Inject]
protected IAIModels_Repositories _aIModels_Repositories { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await InitData();
}
private async Task InitData(string searchKey=null)
{
if (string.IsNullOrEmpty(searchKey))
{
_data = _aIModels_Repositories.GetList();
}
else
{
_data = _aIModels_Repositories.GetList(p=>p.ModelName.Contains(searchKey)||p.ModelDescription.Contains(searchKey));
}
await InvokeAsync(StateHasChanged);
}
public async Task OnSearch() {
await InitData(_searchKeyword);
}
public async Task AddModel() {
NavigationManager.NavigateTo("/setting/model/add");
}
public void Edit(string modelid)
{
NavigationManager.NavigateTo("/setting/model/add/"+ modelid);
}
public async Task Delete(string modelid)
{
var content = "是否确认删除此模型";
var title = "删除";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
await _aIModels_Repositories.DeleteAsync(modelid);
await InitData("");
}
}
}
}

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

@@ -0,0 +1,60 @@
@namespace AntSK.Pages.Setting.User
@using AntSK.Domain.Repositories
@using AntSK.Models
@page "/setting/user/add"
@page "/setting/user/add/{UserId}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
<PageContainer Title="新增用户">
<ChildContent>
<Card>
<Form Model="@_userModel"
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="工号" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入用户工号" @bind-Value="@context.No" />
</FormItem>
<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 Type="password" Placeholder="请输入用户密码" @bind-Value="@context.Password" />
</FormItem>
<FormItem Label="用户备注" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入用户备注" @bind-Value="@context.Describe" />
</FormItem>
<FormItem Label="菜单权限" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select Mode="multiple"
@bind-Values="_menuKeys"
Placeholder="选择菜单权限"
TItemValue="string"
TItem="string"
Size="@AntSizeLDSType.Default">
<SelectOptions>
@foreach (var menu in menuList)
{
<SelectOption TItem="string" TItemValue="string" Value="@menu.Key" Label="@menu.Name" />
}
</SelectOptions>
</Select>
</FormItem>
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" OnClick="HandleSubmit">
保存
</Button>
<Button OnClick="Back">
返回
</Button>
</FormItem>
</Form>
</Card>
</ChildContent>
</PageContainer>
@code {
}

View File

@@ -0,0 +1,76 @@
using AntDesign;
using AntDesign.ProLayout;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using DocumentFormat.OpenXml.InkML;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.AspNetCore.Components;
namespace AntSK.Pages.Setting.User
{
public partial class AddUser
{
[Parameter]
public string UserId { get; set; }
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
[Inject] protected MessageService? Message { get; set; }
[Inject] public HttpClient HttpClient { get; set; }
private Users _userModel = new Users();
private string _password = "";
IEnumerable<string> _menuKeys;
private List<MenuDataItem> menuList = new List<MenuDataItem>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (!string.IsNullOrEmpty(UserId))
{
_userModel= _users_Repositories.GetFirst(p => p.Id == UserId);
_password= _userModel.Password;
}
menuList = (await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json")).ToList().Where(p=>p.Key!= "setting").ToList();
_menuKeys= _userModel.MenuRole?.Split(",");
}
private void HandleSubmit()
{
_userModel.MenuRole = string.Join(",", _menuKeys);
if (string.IsNullOrEmpty(UserId))
{
//新增
_userModel.Id = Guid.NewGuid().ToString();
if (_userModel.No == LoginOption.User)
{
_ = Message.Error("工号不能为管理员账号!", 2);
return;
}
if (_users_Repositories.IsAny(p => p.No == _userModel.No))
{
_ = Message.Error("工号已存在!", 2);
return;
}
_userModel.Password=PasswordUtil.HashPassword(_userModel.Password);
_users_Repositories.Insert(_userModel);
}
else
{
//修改
if (_userModel.Password!=_password)
{
_userModel.Password = PasswordUtil.HashPassword(_userModel.Password);
}
_users_Repositories.Update(_userModel);
}
Back();
}
private void Back()
{
NavigationManager.NavigateTo("/setting/userlist");
}
}
}

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