Compare commits

...

62 Commits
0.2.9 ... 0.3.4

Author SHA1 Message Date
zyxucp
73b65f7305 Merge pull request #84 from AIDotNet/feature_llamasharp
Feature llamasharp
2024-04-28 20:38:01 +08:00
zyxucp
0ea52eced9 fix 修改聊天为chathistory 2024-04-28 20:37:37 +08:00
zyxucp
498e9ba9f6 Merge branch 'main' into feature_llamasharp 2024-04-28 20:24:30 +08:00
zyxucp
125695665b add 修改水印影响样式问题 2024-04-28 16:53:10 +08:00
zyxucp
0e08b3ae85 add 水印 2024-04-28 15:37:51 +08:00
zyxucp
7cb8f99e7e fix 处理聊天对话 2024-04-27 23:03:46 +08:00
zyxucp
d15cb527d0 add 教程视频 2024-04-24 23:56:45 +08:00
zyxucp
9cb36174fd Update README.md 2024-04-23 14:16:50 +08:00
zyxucp
6265f94ef2 fix 修改文档导入问答单独的index 2024-04-23 13:32:57 +08:00
zyxucp
09d90b654c fix 处理文档问答问题 2024-04-23 13:28:38 +08:00
zyxucp
64e2bca2e6 Merge pull request #80 from AIDotNet/fix_chatkmsbug
fix 修改聊天记录知识库保存bug
2024-04-23 12:54:49 +08:00
zyxucp
328ece6d73 fix 修改聊天记录知识库保存bug 2024-04-23 12:53:51 +08:00
zyxucp
fabb8c2044 fix 处理合并 2024-04-23 11:51:08 +08:00
zyxucp
6ca75df880 fix 处理合并 2024-04-23 11:50:46 +08:00
zyxucp
3d4dfaced1 margin 2024-04-23 11:48:43 +08:00
zyxucp
d532bf3bb6 add 聊天历史记录搜索 2024-04-22 23:50:08 +08:00
zyxucp
e1fd288875 fix 样式修改 2024-04-22 23:37:20 +08:00
zyxucp
91eae9cfa8 fix 修改聊天记录存储 2024-04-22 23:31:08 +08:00
zyxucp
b0059942d3 Merge pull request #79 from AIDotNet/feature_chat
Feature chat
2024-04-22 23:17:58 +08:00
zyxucp
a716982878 add 聊天记录 2024-04-22 23:17:16 +08:00
zyxucp
3d4e48f9f5 fix 修改错误 2024-04-22 22:26:45 +08:00
zyxucp
1f212d3156 update semantic kernel to 1.8.0 2024-04-22 22:19:46 +08:00
zyxucp
7d91ef6ba1 Update LICENSE 2024-04-22 22:14:44 +08:00
zyxucp
2a450b00de add 处理在有用户时使用chats表次存储聊天记录,匿名访问时使用localstorage存储聊天记录 2024-04-22 22:03:34 +08:00
zyxucp
3a97068248 Update README.md 2024-04-21 12:12:46 +08:00
zeyu xu
1d9d95899a update 更新antsk logo 2024-04-21 11:17:51 +08:00
zyxucp
7ae8e52b57 Merge pull request #77 from AIDotNet/feature_bge
update kernelMemory nuget版本
2024-04-21 11:01:06 +08:00
zeyu xu
f5c195a1d0 update kernelMemory nuget版本 2024-04-21 11:00:36 +08:00
zyxucp
78a6b662d3 Update docker-compose.simple.yml 2024-04-20 23:30:27 +08:00
zyxucp
5f814eb76c Update docker-compose.yml 2024-04-20 23:30:07 +08:00
zyxucp
d9e5ebb464 Update README.md 2024-04-20 23:29:48 +08:00
zyxucp
bce0e9183c Update README.md 2024-04-20 23:29:26 +08:00
zyxucp
c40a7bcf22 Merge pull request #76 from AIDotNet/feature_bge
Feature bge
2024-04-20 23:19:10 +08:00
zeyu xu
97a7d447ab add rerank kms 2024-04-20 23:18:07 +08:00
zeyu xu
f803b9538b fix 调整目录 2024-04-20 21:17:27 +08:00
zeyu xu
1ac34c1702 add 应用增加rerank 2024-04-20 21:09:34 +08:00
zeyu xu
e07b480da1 add bgemodel 2024-04-20 21:02:44 +08:00
zeyu xu
9036af57e3 重命名 2024-04-20 20:56:55 +08:00
zeyu xu
93288f9b5c add bgererank 模型下载 2024-04-20 20:56:00 +08:00
zyxucp
f40dd8b013 Merge pull request #75 from AIDotNet/feature_menu
add 模型管理页面 文字超长的样式处理
2024-04-20 10:42:23 +08:00
zeyu xu
c6b83d0695 add 模型管理页面 文字超长的样式处理 2024-04-20 10:41:48 +08:00
zyxucp
592c850198 Merge pull request #74 from AIDotNet/feature_menu
add 单独剥离模型管理菜单
2024-04-20 10:31:43 +08:00
zeyu xu
4a3930ac7b add 单独剥离模型管理菜单 2024-04-20 10:31:19 +08:00
zyxucp
c05ba0af3e Update README.md 2024-04-19 23:20:44 +08:00
zyxucp
630ee51df6 Update docker-compose.simple.yml 2024-04-19 23:20:26 +08:00
zyxucp
d0e75e26c3 Update docker-compose.yml 2024-04-19 23:20:04 +08:00
zyxucp
62c36c3072 Merge pull request #73 from AIDotNet/feature_deldimensions
add DelDimensions
2024-04-19 23:09:33 +08:00
zeyu xu
baef309064 add DelDimensions 2024-04-19 23:08:50 +08:00
zeyu xu
d717cbad9c update nuget sqlsugar 2024-04-19 22:12:02 +08:00
zeyu xu
5ef0624605 fix 修改WithLogCallback 日志输出 2024-04-19 22:04:08 +08:00
zyxucp
af2930a371 fix 修改WithLog 2024-04-19 18:37:03 +08:00
zyxucp
98f0f9fe84 Update README.md 2024-04-18 22:15:30 +08:00
zeyu xu
28a23271e9 fix 文件名修改 2024-04-18 22:05:23 +08:00
zeyu xu
f1ba0bdf10 add 模型删除校验 2024-04-18 21:49:09 +08:00
zeyu xu
0d5513f374 add Directory.Build.props 2024-04-18 21:23:25 +08:00
zyxucp
4812cc308c Update README.md 2024-04-17 22:59:46 +08:00
zyxucp
584f7faded add 环境变量 2024-04-17 18:25:58 +08:00
zyxucp
08dcef2d8b Update docker-compose.simple.yml 2024-04-16 21:56:58 +08:00
zyxucp
68218733a2 Update docker-compose.yml 2024-04-16 21:56:40 +08:00
zyxucp
eb64cbf3d4 update 升级km nuget版本 2024-04-16 17:01:32 +08:00
zyxucp
f0e8a55522 Merge pull request #72 from AIDotNet/feature_qa1
fix 修改切分使用服务注入
2024-04-16 13:56:39 +08:00
zyxucp
5ec5a0bde4 fix 修改切分使用服务注入 2024-04-16 13:54:24 +08:00
56 changed files with 1539 additions and 367 deletions

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright [2024] [许泽宇]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -8,7 +8,7 @@
- **内存内核 (Kernel Memory)**具备持续学习和存储知识点的能力AntSK 拥有长期记忆功能,累积经验,提供更个性化的交互体验。
- **知识库**通过文档Word、PDF、Excel、Txt、Markdown、Json、PPT等形式导入知识库可以进行知识库问答。
- **知识库**通过文档Word、PDF、Excel、Txt、Markdown、Json、PPT等形式导入知识库可以进行知识库问答支持本地bge-embedding 向量模型 以及bge-rerank 重排模型
- **文生图**:集成**StableDiffusion** 本地模型,可以进行文生图。
@@ -42,15 +42,19 @@ AntSK 适用于多种业务场景,例如:
## ✏️功能示例
### 在线演示
```
https://antsk.ai-dotnet.com/
```
[文档地址](http://antsk.cn/)
[体验地址](https://antsk.ai-dotnet.com/)
```
默认账号test
默认密码test
由于云服务器配置较低,无法运行本地模型,所以把系统设置权限关闭了,大家看看界面即可,要使用本地模型,请下载自行使用
请勿在演示站点上传敏感信息
```
### 其他功能示例
@@ -89,7 +93,7 @@ version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/AIDotNet/antsk:v0.2.7
image: registry.cn-hangzhou.aliyuncs.com/AIDotNet/antsk:v0.3.1
ports:
- 5000:5000
networks:
@@ -210,7 +214,7 @@ DB我使用的是CodeFirst模式只要配置好数据库链接表结构是
想了解更多信息或开始使用 **AntSK**,可以关注我的公众号以及加入交流群。
## ☎️联系我
如有任何问题或建议,请通过以下方式关注我的公众号,发消息与我联系,我们也有交流群,可以发送进群等消息,然后我会拉你进交流群
如有任何问题或建议,请通过以下方式关注我的公众号《许泽宇的技术分享》,发消息与我联系,我们也有AIDotnet交流群,可以发送进群等消息,然后我会拉你进交流群
![公众号](https://github.com/AIDotNet/AntSK/blob/main/images/gzh.jpg)
## 🌟 Star History

View File

@@ -3,9 +3,9 @@ version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.7
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.3.1
# 如果需要pytorch环境需要使用下面这个镜像镜像比较大
# image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:p0.2.7
# image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:p0.3.1
ports:
- 5000:5000
networks:

View File

@@ -18,9 +18,9 @@ services:
- ./pg/data:/var/lib/postgresql/data
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.7
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.3.1
# 如果需要pytorch环境需要使用下面这个镜像镜像比较大
# image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:p0.2.7
# image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:p0.3.1
ports:
- 5000:5000
networks:

View File

@@ -0,0 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<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="AntDesign.Charts" Version="0.5.1" />
<PackageReference Include="AntDesign.ProLayout" Version="0.18.2" />
<PackageReference Include="BlazorComponents.Terminal" Version="0.6.0" />
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="pythonnet" Version="3.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="AutoMapper" Version="8.1.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Markdig" Version="0.37.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.151" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
<PackageReference Include="RestSharp" Version="110.2.0" />
<PackageReference Include="NPOI" Version="2.7.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.7.1" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.7.1" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.7.1-alpha" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.36.240415.2" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.36.240415.2" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Qdrant" Version="0.36.240415.2" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Redis" Version="0.36.240415.2" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.AzureAISearch" Version="0.36.240415.2" />
<PackageReference Include="LLamaSharp" Version="0.11.2" />
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.11.2" />
<PackageReference Include="LLamaSharp.Backend.Cuda12" Version="0.11.2" />
<PackageReference Include="LLamaSharp.kernel-memory" Version="0.11.2" />
<PackageReference Include="LLamaSharp.semantic-kernel" Version="0.11.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AntSK.LLamaFactory\AntSK.LLamaFactory.csproj" />
<ProjectReference Include="..\AntSk.LLM\AntSK.LLM.csproj" />
<ProjectReference Include="..\AntSK.OCR\AntSK.OCR.csproj" />
<ProjectReference Include="..\MiddleWare\AntSK.BackgroundTask\AntSK.BackgroundTask.csproj" />
</ItemGroup>
</Project>

View File

@@ -19,27 +19,27 @@
<PackageReference Include="AutoMapper" Version="8.1.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Markdig" Version="0.36.2" />
<PackageReference Include="Markdig" Version="0.37.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.151" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.152" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
<PackageReference Include="RestSharp" Version="110.2.0" />
<PackageReference Include="NPOI" Version="2.7.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.7.1" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.7.1" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.7.1-alpha" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.35.240321.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.35.240321.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Qdrant" Version="0.35.240321.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Redis" Version="0.35.240321.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.AzureAISearch" Version="0.35.240321.1" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.8.0" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.8.0" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.8.0-alpha" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="$(KMVersion)" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="$(KMVersion)" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Qdrant" Version="$(KMVersion)" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Redis" Version="$(KMVersion)" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.AzureAISearch" Version="$(KMVersion)" />
<PackageReference Include="LLamaSharp" Version="0.11.2" />
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.11.2" />
<PackageReference Include="LLamaSharp.Backend.Cuda12" Version="0.11.2" />
<PackageReference Include="LLamaSharp.kernel-memory" Version="0.11.2" />
<PackageReference Include="LLamaSharp.semantic-kernel" Version="0.11.2" />
<PackageReference Include="LLamaSharp" Version="$(LLamaSharpVersion)" />
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="$(LLamaSharpVersion)" />
<PackageReference Include="LLamaSharp.Backend.Cuda12" Version="$(LLamaSharpVersion)" />
<PackageReference Include="LLamaSharp.kernel-memory" Version="$(LLamaSharpVersion)" />
<PackageReference Include="LLamaSharp.semantic-kernel" Version="$(LLamaSharpVersion)" />
</ItemGroup>

View File

@@ -157,11 +157,6 @@
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.MessageInfo.IsSend">
<summary>
发送是true 接收是false
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.PageIndex">
<summary>
当前页从1开始
@@ -177,7 +172,12 @@
总数
</summary>
</member>
<member name="M:AntSK.Domain.Domain.Other.EmbeddingConfig.LoadModel(System.String,System.String)">
<member name="M:AntSK.Domain.Domain.Other.Bge.BegRerankConfig.LoadModel(System.String,System.String)">
<summary>
模型写死
</summary>
</member>
<member name="M:AntSK.Domain.Domain.Other.Bge.BgeEmbeddingConfig.LoadModel(System.String,System.String)">
<summary>
模型写死
</summary>
@@ -199,7 +199,7 @@
<member name="M:AntSK.Domain.Domain.Other.QAHandler.InvokeAsync(Microsoft.KernelMemory.Pipeline.DataPipeline,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:AntSK.Domain.Domain.Service.ChatService.SendChatByAppAsync(AntSK.Domain.Repositories.Apps,System.String,Microsoft.SemanticKernel.ChatCompletion.ChatHistory)">
<member name="M:AntSK.Domain.Domain.Service.ChatService.SendChatByAppAsync(AntSK.Domain.Repositories.Apps,Microsoft.SemanticKernel.ChatCompletion.ChatHistory)">
<summary>
发送消息
</summary>
@@ -402,6 +402,36 @@
回答最大token数
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Chats.UserName">
<summary>
用户名
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Chats.AppId">
<summary>
应用ID
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Chats.Context">
<summary>
消息内容
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Chats.IsSend">
<summary>
发送是true 接收是false
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Chats.CreateTime">
<summary>
创建事件
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Chats.FileName">
<summary>
文件名
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Funs.Path">
<summary>
接口描述

View File

@@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Domain.Other.Bge;
namespace AntSK.Domain.Common.Embedding
{
@@ -22,12 +22,12 @@ namespace AntSK.Domain.Common.Embedding
public HuggingfaceTextEmbeddingGenerator(string pyDllPath,string modelName)
{
_embedder = EmbeddingConfig.LoadModel(pyDllPath, modelName);
_embedder = BgeEmbeddingConfig.LoadModel(pyDllPath, modelName);
}
public void Dispose()
{
EmbeddingConfig.Dispose();
BgeEmbeddingConfig.Dispose();
}
//public async Task<IList<ReadOnlyMemory<float>>> GenerateEmbeddingAsync(IList<string> data, CancellationToken cancellationToken = default)
@@ -44,13 +44,13 @@ namespace AntSK.Domain.Common.Embedding
public async Task<Microsoft.KernelMemory.Embedding> GenerateEmbeddingAsync(string text, CancellationToken cancellationToken = default)
{
var embeddings = await EmbeddingConfig.GetEmbedding(text);
var embeddings = await BgeEmbeddingConfig.GetEmbedding(text);
return new Microsoft.KernelMemory.Embedding(embeddings);
}
public int CountTokens(string text)
{
return EmbeddingConfig.TokenCount(text);
return BgeEmbeddingConfig.TokenCount(text);
}
}
}

View File

@@ -0,0 +1,8 @@
<Project>
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<PropertyGroup>
<KMVersion>0.37.240420.2</KMVersion>
<LLamaSharpVersion>0.11.2</LLamaSharpVersion>
</PropertyGroup>
</Project>

View File

@@ -14,10 +14,10 @@ namespace AntSK.Domain.Domain.Interface
{
public interface IChatService
{
IAsyncEnumerable<StreamingKernelContent> SendChatByAppAsync(Apps app, string questions, ChatHistory history);
IAsyncEnumerable<string> SendChatByAppAsync(Apps app, ChatHistory history);
IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, ChatHistory history, string filePath, List<RelevantSource> relevantSources = null);
Task<string> SendImgByAppAsync(Apps app, string questions);
Task<ChatHistory> GetChatHistory(List<MessageInfo> MessageList);
Task<ChatHistory> GetChatHistory(List<Chats> MessageList, ChatHistory history);
}
}

View File

@@ -9,7 +9,10 @@ namespace AntSK.Domain.Domain.Model.Constant
public class KmsConstantcs
{
public const string KmsIdTag = "kmsid";
public const string FileIdTag = "fileid";
public const string AppIdTag = "appid";
public const string KmsIndex = "kms";
public const string FileIndex = "kms";
public const string KmsSearchNull="知识库未搜索到相关内容";
public const string KmsPrompt = @"使用<data></data>标记的内容作为你的知识:

View File

@@ -8,6 +8,8 @@ namespace AntSK.Domain.Domain.Model.Dto
public string Text { get; set; }
public float Relevance { get; set; }
public double RerankScore { get; set; }
public override string ToString()
{
return $"[file:{SourceName};Relevance:{(Relevance * 100):F2}%]:{Text}";

View File

@@ -26,8 +26,10 @@ namespace AntSK.Domain.Domain.Model.Enum
LLamaFactory = 6,
[Display(Name = "Bge Embedding")]
BgeEmbedding = 7,
[Display(Name = "Bge Rerank")]
BgeRerank = 8,
[Display(Name = "StableDiffusion")]
StableDiffusion = 8,
StableDiffusion = 9,
[Display(Name = "模拟输出")]
Mock = 100,
@@ -41,5 +43,6 @@ namespace AntSK.Domain.Domain.Model.Enum
Chat = 1,
Embedding = 2,
Image=3,
Rerank=4
}
}

View File

@@ -1,17 +0,0 @@
namespace AntSK.Domain.Domain.Model
{
public class MessageInfo
{
public string ID { get; set; } = "";
public string Context { get; set; } = "";
/// <summary>
/// 发送是true 接收是false
/// </summary>
public bool IsSend { get; set; } = false;
public DateTime CreateTime { get; set; }
public string? FileName { get; set; }
}
}

View File

@@ -0,0 +1,81 @@
using Newtonsoft.Json;
using Python.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Python.Runtime.Py;
namespace AntSK.Domain.Domain.Other.Bge
{
public static class BegRerankConfig
{
public static dynamic model { get; set; }
static object lockobj = new object();
/// <summary>
/// 模型写死
/// </summary>
public static dynamic LoadModel(string pythondllPath, string modelName)
{
lock (lockobj)
{
if (model == null)
{
if (string.IsNullOrEmpty(Runtime.PythonDLL))
{
Runtime.PythonDLL = pythondllPath;
}
PythonEngine.Initialize();
try
{
using (GIL())// 初始化Python环境的Global Interpreter Lock)
{
dynamic modelscope = Py.Import("modelscope");
dynamic flagEmbedding = Py.Import("FlagEmbedding");
dynamic model_dir = modelscope.snapshot_download(modelName, revision: "master");
dynamic flagReranker = flagEmbedding.FlagReranker(model_dir, use_fp16: true);
model = flagReranker;
return model;
}
}
catch (Exception ex)
{
throw ex;
}
}
else
{
return model;
}
}
}
public static double Rerank(List<string> list)
{
using (GIL())
{
try
{
PyList pyList = new PyList();
foreach (string item in list)
{
pyList.Append(item.ToPython()); // 将C# string转换为Python对象并添加到PyList中
}
PyObject result = model.compute_score(pyList, normalize: true);
return result.As<double>();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
}

View File

@@ -7,9 +7,9 @@ using System.Text;
using System.Threading.Tasks;
using static Python.Runtime.Py;
namespace AntSK.Domain.Domain.Other
namespace AntSK.Domain.Domain.Other.Bge
{
public static class EmbeddingConfig
public static class BgeEmbeddingConfig
{
public static dynamic model { get; set; }
@@ -27,18 +27,20 @@ namespace AntSK.Domain.Domain.Other
if (model == null)
{
//Runtime.PythonDLL = @"D:\Programs\Python\Python311\python311.dll";
Runtime.PythonDLL = pythondllPath;
if (string.IsNullOrEmpty(Runtime.PythonDLL))
{
Runtime.PythonDLL = pythondllPath;
}
PythonEngine.Initialize();
PythonEngine.BeginAllowThreads();
try
{
using (Py.GIL())// 初始化Python环境的Global Interpreter Lock)
using (GIL())// 初始化Python环境的Global Interpreter Lock)
{
dynamic modelscope = Py.Import("modelscope");
dynamic modelscope = Import("modelscope");
//dynamic model_dir = modelscope.snapshot_download("AI-ModelScope/bge-large-zh-v1.5", revision: "master");
dynamic model_dir = modelscope.snapshot_download(modelName, revision: "master");
dynamic HuggingFaceBgeEmbeddingstemp = Py.Import("langchain_community.embeddings.huggingface");
dynamic HuggingFaceBgeEmbeddingstemp = Import("langchain_community.embeddings.huggingface");
dynamic HuggingFaceBgeEmbeddings = HuggingFaceBgeEmbeddingstemp.HuggingFaceBgeEmbeddings;
string model_name = model_dir;
dynamic model_kwargs = new PyDict();
@@ -51,7 +53,7 @@ namespace AntSK.Domain.Domain.Other
return hugginmodel;
}
}
catch(Exception ex)
catch (Exception ex)
{
throw ex;
}
@@ -63,7 +65,7 @@ namespace AntSK.Domain.Domain.Other
public static Task<float[]> GetEmbedding(string queryStr)
{
using (Py.GIL())
using (GIL())
{
PyObject queryResult = model.embed_query(queryStr);
var floatList = queryResult.As<float[]>();

View File

@@ -1,4 +1,6 @@
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.KernelMemory.AI.OpenAI;
@@ -7,10 +9,12 @@ using Microsoft.KernelMemory.DataFormats.Text;
using Microsoft.KernelMemory.Diagnostics;
using Microsoft.KernelMemory.Extensions;
using Microsoft.KernelMemory.Pipeline;
using Microsoft.SemanticKernel;
using Newtonsoft.Json;
using RestSharp;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
namespace AntSK.Domain.Domain.Other
{
@@ -20,9 +24,11 @@ namespace AntSK.Domain.Domain.Other
private readonly IPipelineOrchestrator _orchestrator;
private readonly ILogger<QAHandler> _log;
private readonly TextChunker.TokenCounter _tokenCounter;
private readonly IKernelService _kernelService;
public QAHandler(
string stepName,
IPipelineOrchestrator orchestrator,
IKernelService kernelService,
TextPartitioningOptions? options = null,
ILogger<QAHandler>? log = null
)
@@ -34,6 +40,7 @@ namespace AntSK.Domain.Domain.Other
this._log = log ?? DefaultLogger<QAHandler>.Instance;
this._tokenCounter = DefaultGPTTokenizer.StaticCountTokens;
this._kernelService = kernelService;
}
/// <inheritdoc />
@@ -88,15 +95,27 @@ namespace AntSK.Domain.Domain.Other
this._log.LogDebug("Partitioning text file {0}", file.Name);
string content = partitionContent.ToString();
using (HttpClient httpclient = new HttpClient())
var kernel = _kernelService.GetKernelByAIModelID(StepName);
var lines = TextChunker.SplitPlainTextLines(content, 299);
var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, 3000);
KernelFunction jsonFun = kernel.Plugins.GetFunction("KMSPlugin", "QA");
List<string> qaList = new List<string>();
foreach (var para in paragraphs)
{
httpclient.Timeout = TimeSpan.FromMinutes(10);
StringContent scontent = new StringContent(JsonConvert.SerializeObject(new QAModel() { ChatModelId = StepName, Context = content }), Encoding.UTF8, "application/json");
HttpResponseMessage response = await httpclient.PostAsync("http://localhost:5000/api/KMS/QA", scontent);
List<string> qaList = JsonConvert.DeserializeObject<List<string>>( await response.Content.ReadAsStringAsync());
sentences = qaList;
partitions = qaList;
var qaresult = await kernel.InvokeAsync(function: jsonFun, new KernelArguments() { ["input"] = para });
var qaListStr = qaresult.GetValue<string>().ConvertToString();
string pattern = @"Q\d+:.*?A\d+:.*?(?=(Q\d+:|$))";
RegexOptions options = RegexOptions.Singleline;
foreach (Match match in Regex.Matches(qaListStr, pattern, options))
{
qaList.Add(match.Value.Trim()); // Trim用于删除可能的首尾空格
}
}
sentences = qaList;
partitions = qaList;
break;
}
default:

View File

@@ -3,12 +3,14 @@ using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Constant;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Domain.Other.Bge;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using AntSK.LLM.StableDiffusion;
using Markdig;
using Microsoft.KernelMemory;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.Diagnostics;
using System.Drawing;
@@ -34,45 +36,54 @@ namespace AntSK.Domain.Domain.Service
/// <param name="questions"></param>
/// <param name="history"></param>
/// <returns></returns>
public async IAsyncEnumerable<StreamingKernelContent> SendChatByAppAsync(Apps app, string questions, ChatHistory history)
public async IAsyncEnumerable<string> SendChatByAppAsync(Apps app, ChatHistory history)
{
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
{
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
}
KernelArguments args = new KernelArguments();
if (history.Count > 10)
{
app.Prompt = @"${{ConversationSummaryPlugin.SummarizeConversation $history}}" + app.Prompt;
args = new() {
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) },
{ "input", questions }
};
}
else
{
args = new()
{
{ "input", $"{string.Join("\n", history.Select(x => x.Role + ": " + x.Content))}{Environment.NewLine} user:{questions}" }
};
}
var _kernel = _kernelService.GetKernelByApp(app);
var chat = _kernel.GetRequiredService<IChatCompletionService>();
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
List<string> completionList = new List<string>();
if (!string.IsNullOrEmpty(app.ApiFunctionList) || !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
settings.ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions;
while (true)
{
ChatMessageContent result = await chat.GetChatMessageContentAsync(history, settings, _kernel);
if (result.Content is not null)
{
string chunkCompletion = result.Content.ConvertToString();
completionList.Add(chunkCompletion);
foreach (var content in completionList)
{
yield return content.ConvertToString();
}
break;
}
history.Add(result);
IEnumerable<FunctionCallContent> functionCalls = FunctionCallContent.GetFunctionCalls(result);
if (!functionCalls.Any())
{
break;
}
foreach (var functionCall in functionCalls)
{
FunctionResultContent resultContent = await functionCall.InvokeAsync(_kernel);
history.Add(resultContent.ToChatMessage());
}
}
}
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
var chatResult = _kernel.InvokeStreamingAsync(function: func,
arguments: args);
await foreach (var content in chatResult)
else
{
yield return content;
var chatResult = chat.GetStreamingChatMessageContentsAsync(history, settings, _kernel);
await foreach (var content in chatResult)
{
yield return content.ConvertToString();
}
}
}
@@ -84,40 +95,103 @@ namespace AntSK.Domain.Domain.Service
if (!string.IsNullOrWhiteSpace(filePath))
{
var memory = _kMService.GetMemoryByApp(app);
var fileId = Guid.NewGuid().ToString();
var result = await memory.ImportDocumentAsync(new Microsoft.KernelMemory.Document(fileId).AddFile(filePath)
.AddTag(KmsConstantcs.KmsIdTag, app.Id)
, index: KmsConstantcs.KmsIndex);
var filters = new MemoryFilter().ByTag(KmsConstantcs.KmsIdTag, app.Id);
// 匹配GUID的正则表达式
string pattern = @"\b[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\b";
var searchResult = await memory.SearchAsync(questions, index: KmsConstantcs.KmsIndex, filters: [filters]);
relevantSourceList.AddRange(searchResult.Results.SelectMany(item => item.Partitions.Select(part => new RelevantSource()
// 使用正则表达式找到匹配
Match match = Regex.Match(filePath, pattern);
if (match.Success)
{
SourceName = item.SourceName,
Text = Markdown.ToHtml(part.Text),
Relevance = part.Relevance
})));
var fileId = match.Value;
var status=await memory.IsDocumentReadyAsync(fileId, index: KmsConstantcs.KmsIndex);
if (!status)
{
var result = await memory.ImportDocumentAsync(new Document(fileId).AddFile(filePath)
.AddTag(KmsConstantcs.AppIdTag, app.Id)
.AddTag(KmsConstantcs.FileIdTag, fileId)
, index: KmsConstantcs.FileIndex);
}
var filters = new List<MemoryFilter>() {
new MemoryFilter().ByTag(KmsConstantcs.AppIdTag, app.Id),
new MemoryFilter().ByTag(KmsConstantcs.FileIdTag, fileId)
};
var searchResult = await memory.SearchAsync(questions, index: KmsConstantcs.FileIndex, filters: filters);
relevantSourceList.AddRange(searchResult.Results.SelectMany(item => item.Partitions.Select(part => new RelevantSource()
{
SourceName = item.SourceName,
Text = Markdown.ToHtml(part.Text),
Relevance = part.Relevance
})));
app.Prompt = KmsConstantcs.KmsPrompt;
}
}
var dataMsg = new StringBuilder();
if (relevantSourceList.Any())
{
if (!string.IsNullOrEmpty(app.RerankModelID))
{
var rerankModel=_aIModels_Repositories.GetById(app.RerankModelID);
BegRerankConfig.LoadModel(rerankModel.EndPoint, rerankModel.ModelName);
//进行rerank
foreach (var item in relevantSourceList)
{
List<string> rerank = new List<string>();
rerank.Add(questions);
rerank.Add(item.Text);
item.RerankScore = BegRerankConfig.Rerank(rerank);
}
relevantSourceList = relevantSourceList.OrderByDescending(p => p.RerankScore).Take(app.MaxMatchesCount).ToList();
}
bool isSearch = false;
foreach (var item in relevantSourceList)
{
//匹配相似度
if (item.Relevance >= app.Relevance / 100)
if (!string.IsNullOrEmpty(app.RerankModelID))
{
dataMsg.AppendLine(item.ToString());
isSearch = true;
//匹配重排后相似度
if (item.RerankScore >= app.Relevance / 100)
{
dataMsg.AppendLine(item.ToString());
isSearch = true;
}
}
else
{
//匹配相似度
if (item.Relevance >= app.Relevance / 100)
{
dataMsg.AppendLine(item.ToString());
isSearch = true;
}
}
}
//处理markdown显示
relevantSources?.AddRange(relevantSourceList);
Dictionary<string, string> fileDic = new Dictionary<string, string>();
foreach (var item in relevantSourceList)
{
if (fileDic.ContainsKey(item.SourceName))
{
item.SourceName = fileDic[item.SourceName];
}
else
{
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == item.SourceName);
if (fileDetail.IsNotNull())
{
string fileName = fileDetail.FileName;
fileDic.Add(item.SourceName, fileName);
item.SourceName = fileName;
}
}
item.Text = Markdown.ToHtml(item.Text);
}
@@ -126,7 +200,7 @@ namespace AntSK.Domain.Domain.Service
//KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask1");
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
var func = _kernel.CreateFunctionFromPrompt(app.Prompt , settings);
var chatResult = _kernel.InvokeStreamingAsync(function: func,
arguments: new KernelArguments() { ["doc"] = dataMsg.ToString(), ["history"] = string.Join("\n", history.Select(x => x.Role + ": " + x.Content)), ["input"] = questions });
@@ -254,9 +328,8 @@ namespace AntSK.Domain.Domain.Service
}
}
public async Task<ChatHistory> GetChatHistory(List<MessageInfo> MessageList)
public async Task<ChatHistory> GetChatHistory(List<Chats> MessageList, ChatHistory history)
{
ChatHistory history = new ChatHistory();
if (MessageList.Count > 1)
{

View File

@@ -29,7 +29,8 @@ namespace AntSK.Domain.Domain.Service
public class KMService(
IKmss_Repositories _kmss_Repositories,
IAIModels_Repositories _aIModels_Repositories,
IMessageService? _message
IMessageService? _message,
IKernelService _kernelService
) : IKMService
{
private MemoryServerless _memory;
@@ -44,14 +45,30 @@ namespace AntSK.Domain.Domain.Service
var embedModel = _aIModels_Repositories.GetFirst(p => p.Id == app.EmbeddingModelID);
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
var embeddingHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(embedModel.EndPoint);
var searchClientConfig = new SearchClientConfig
SearchClientConfig searchClientConfig;
if (string.IsNullOrEmpty(app.RerankModelID))
{
MaxAskPromptSize = app.MaxAskPromptSize,
MaxMatchesCount = app.MaxMatchesCount,
AnswerTokens = app.AnswerTokens,
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
//不重排直接取查询数
searchClientConfig = new SearchClientConfig
{
MaxAskPromptSize = app.MaxAskPromptSize,
MaxMatchesCount = app.MaxMatchesCount,
AnswerTokens = app.AnswerTokens,
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
}
else
{
//重排取rerank数
searchClientConfig = new SearchClientConfig
{
MaxAskPromptSize = app.MaxAskPromptSize,
MaxMatchesCount = app.RerankCount,
AnswerTokens = app.AnswerTokens,
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
}
var memoryBuild = new KernelMemoryBuilder()
.WithSearchClientConfig(searchClientConfig)
@@ -115,7 +132,7 @@ namespace AntSK.Domain.Domain.Service
//加载向量库
WithMemoryDbByVectorDB(memoryBuild);
_memory = memoryBuild.Build<MemoryServerless>();
_memory = memoryBuild.AddSingleton<IKernelService>(_kernelService).Build<MemoryServerless>();
return _memory;
}
//else {

View File

@@ -20,6 +20,8 @@ using System.Reflection;
using DocumentFormat.OpenXml.Drawing;
using Microsoft.KernelMemory;
using OpenCvSharp.ML;
using LLamaSharp.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.ChatCompletion;
namespace AntSK.Domain.Domain.Service
{
@@ -105,11 +107,13 @@ namespace AntSK.Domain.Domain.Service
var (weights, parameters) = LLamaConfig.GetLLamaConfig(chatModel.ModelName);
var ex = new StatelessExecutor(weights, parameters);
builder.Services.AddKeyedSingleton<ITextGenerationService>("local-llama", new LLamaSharpTextCompletion(ex));
builder.Services.AddKeyedSingleton<IChatCompletionService>("local-llama-chat", new LLamaSharpChatCompletion(ex));
break;
case Model.Enum.AIType.SparkDesk:
var options = new SparkDeskOptions { AppId = chatModel.EndPoint, ApiSecret = chatModel.ModelKey, ApiKey = chatModel.ModelName, ModelVersion = Sdcb.SparkDesk.ModelVersion.V3_5 };
builder.Services.AddKeyedSingleton<ITextGenerationService>("spark-desk", new SparkDeskTextCompletion(options, chatModel.Id));
builder.Services.AddKeyedSingleton<IChatCompletionService>("spark-desk-chat", new SparkDeskChatCompletion(options, chatModel.Id));
break;
case Model.Enum.AIType.DashScope:
@@ -118,6 +122,7 @@ namespace AntSK.Domain.Domain.Service
case Model.Enum.AIType.Mock:
builder.Services.AddKeyedSingleton<ITextGenerationService>("mock", new MockTextCompletion());
builder.Services.AddKeyedSingleton<IChatCompletionService>("mock-chat", new MockChatCompletion());
break;
case Model.Enum.AIType.LLamaFactory:
builder.AddOpenAIChatCompletion(

View File

@@ -92,9 +92,9 @@ namespace AntSK.Domain.Domain.Service
WorkingDirectory = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "llamafactory"),
}
};
process.StartInfo.Environment["CUDA_VISIBLE_DEVICES"] = "0";
process.StartInfo.Environment["CUDA_VISIBLE_DEVICES"] = Environment.GetEnvironmentVariable("CUDA_VISIBLE_DEVICES") ?? "0";
process.StartInfo.Environment["API_PORT"] = "8000";
process.StartInfo.EnvironmentVariables["USE_MODELSCOPE_HUB"] = "1";
process.StartInfo.EnvironmentVariables["USE_MODELSCOPE_HUB"] = Environment.GetEnvironmentVariable("USE_MODELSCOPE_HUB") ?? "1";
process.OutputDataReceived += (sender, eventArgs) =>
{
Console.WriteLine($"{eventArgs.Data}");

View File

@@ -44,6 +44,9 @@ namespace AntSK.Domain.Repositories
/// </summary>
public string? EmbeddingModelID { get; set; }
public string? RerankModelID { get; set; }
public string? ImageModelID { get; set; }
/// <summary>
/// 温度
@@ -96,6 +99,9 @@ namespace AntSK.Domain.Repositories
[SugarColumn(DefaultValue = "3")]
public int MaxMatchesCount { get; set; } = 3;
[SugarColumn(DefaultValue = "20")]
public int RerankCount { get; set; } = 20;
/// <summary>
/// 回答最大token数
/// </summary>

View File

@@ -0,0 +1,41 @@
using AntSK.Domain.Domain.Model.Enum;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace AntSK.Domain.Repositories
{
[SugarTable("Chats")]
public partial class Chats
{
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 应用ID
/// </summary>
public string AppId { get; set; }
/// <summary>
/// 消息内容
/// </summary>
[SugarColumn(ColumnDataType = "varchar(4000)")]
public string Context { get; set; } = "";
/// <summary>
/// 发送是true 接收是false
/// </summary>
public bool IsSend { get; set; } = false;
/// <summary>
/// 创建事件
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 文件名
/// </summary>
public string? FileName { get; set; }
}
}

View File

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

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

View File

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

View File

@@ -17,4 +17,5 @@ matplotlib
fire
modelscope
langchain-community
sentence_transformers
sentence_transformers
FlagEmbedding

View File

@@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.35.240321.1" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="$(KMVersion)" />
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.9.0.20240103" />
<PackageReference Include="Sdcb.OpenCvSharp4.mini.runtime.debian.12-x64" Version="4.8.0.20231125" />
<PackageReference Include="Sdcb.OpenVINO" Version="0.6.4" />

View File

@@ -0,0 +1,8 @@
<Project>
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<PropertyGroup>
<KMVersion>0.36.240416.1</KMVersion>
<LLamaSharpVersion>0.11.2</LLamaSharpVersion>
</PropertyGroup>
</Project>

View File

@@ -27,11 +27,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.LLamaFactory", "AntSK
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.OCR", "AntSK.OCR\AntSK.OCR.csproj", "{6195F7AA-18C2-4372-85CA-11FC4B522686}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{EB227FAB-6060-4271-9A0A-6C99C7965126}"
ProjectSection(SolutionItems) = preProject
NuGet.config = NuGet.config
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

View File

@@ -83,9 +83,7 @@ namespace AntSK.Controllers
{
qaList.Add(match.Value.Trim()); // Trim用于删除可能的首尾空格
}
}
return Ok(qaList);
}
}

View File

@@ -8,7 +8,7 @@
@inject INotificationService _notice
@inherits LayoutComponentBase
<AntDesign.ProLayout.BasicLayout Logo="@("https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg")"
<AntDesign.ProLayout.BasicLayout Logo="@("/assets/logo.svg")"
MenuData="_menuData">
<RightContentRender>
<AntSK.Components.RightContent />

View File

@@ -53,7 +53,7 @@
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
</FormItem>
<FormItem Label="提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea MinRows="4" Placeholder="请输入提示词,用户输入使用{{$input}} 来做占位符" @bind-Value="@context.Prompt" />
<TextArea MinRows="4" Placeholder="请输入角色信息" @bind-Value="@context.Prompt" />
</FormItem>
<FormItem Label="温度系数" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<span>更确定</span>
@@ -61,6 +61,7 @@
<span>更发散</span>
</FormItem>
}
else
{
<FormItem Label="图片模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
@@ -124,6 +125,17 @@
</Select>
<Button Type="@ButtonType.Link" OnClick="NavigateKmsList">去创建</Button>
</FormItem>
<FormItem Label="Rerank重排模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@_rerankList"
@bind-Value="@context.RerankModelID"
ValueProperty="c=>c.Id"
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
</Select>
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
</FormItem>
<FormItem Label="Rerank数" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<AntDesign.InputNumber @bind-Value="context.RerankCount" PlaceHolder="Rerank数"></AntDesign.InputNumber>
</FormItem>
<FormItem Label="提问最大token数" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<AntDesign.InputNumber @bind-Value="context.MaxAskPromptSize" PlaceHolder="提问最大token数"></AntDesign.InputNumber>
</FormItem>

View File

@@ -47,6 +47,7 @@ namespace AntSK.Pages.AppPage
private List<AIModels> _chatList;
private List<AIModels> _embedingList;
private List<AIModels> _rerankList;
private List<AIModels> _imageList;
protected override async Task OnInitializedAsync()
{
@@ -56,6 +57,7 @@ namespace AntSK.Pages.AppPage
var models=_aimodels_Repositories.GetList();
_chatList = models.Where(p => p.AIModelType == AIModelType.Chat).ToList();
_embedingList = models.Where(p => p.AIModelType == AIModelType.Embedding).ToList();
_rerankList = models.Where(p => p.AIModelType == AIModelType.Rerank).ToList();
_imageList = models.Where(p => p.AIModelType == AIModelType.Image).ToList();
_functionService.SearchMarkedMethods();
@@ -141,7 +143,7 @@ namespace AntSK.Pages.AppPage
private void NavigateModelList()
{
NavigationManager.NavigateTo("/setting/modellist");
NavigationManager.NavigateTo("/modelmanager/modellist");
}
private void NavigateKmsList()

View File

@@ -25,7 +25,9 @@
<Body>
@if (!string.IsNullOrEmpty(AppId))
{
<ChatView AppId="@AppId" ShowTitle=false OnRelevantSources="OnRelevantSources"></ChatView>
<Watermark Content="AntSK">
<ChatView AppId="@AppId" ShowTitle=false OnRelevantSources="OnRelevantSources"></ChatView>
</Watermark>
}
</Body>
</Card>
@@ -41,7 +43,14 @@
<Body>
<AntList Bordered DataSource="@_relevantSources" Style="padding:10px;">
<ChildContent Context="item">
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<span> <b>@item.SourceName </b> </span>
<br />
<span>相似度:<Text Mark> @item.Relevance</Text></span>
@if (@item.RerankScore != 0)
{
<br />
<span> Rerank相关性<Text Mark> @item.RerankScore</Text></span>
}
<Body>
@((MarkupString)(@item.Text))
</Body>

View File

@@ -5,6 +5,7 @@
@layout OpenLayout
@inherits AntDomComponentBase
<div id="chat">
@if (ShowTitle)
{
@@ -64,7 +65,7 @@
<Flex Justify="end">
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding"></AntDesign.Input>
@if (app.EmbeddingModelID!=null)
@if (app.EmbeddingModelID != null)
{
<Upload Action="@("api/File/UploadFile")"
Name="file"

View File

@@ -6,14 +6,18 @@ using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using AntSK.LLM.StableDiffusion;
using AntSK.Models;
using Blazored.LocalStorage;
using DocumentFormat.OpenXml.InkML;
using Markdig;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Microsoft.JSInterop;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace AntSK.Pages.ChatPage.Components
{
@@ -36,8 +40,10 @@ namespace AntSK.Pages.ChatPage.Components
[Inject] IChatService _chatService { get; set; }
[Inject] IJSRuntime _JSRuntime { get; set; }
[Inject] ILocalStorageService _localStorage { get; set; }
[Inject] IChats_Repositories _chats_Repositories { get; set; }
[Inject] ProtectedSessionStorage _protectedSessionStore { get; set; }
protected List<MessageInfo> MessageList = [];
protected List<Chats> MessageList = [];
protected string? _messageInput;
protected string _json = "";
protected bool Sendding = false;
@@ -48,25 +54,87 @@ namespace AntSK.Pages.ChatPage.Components
private List<RelevantSource> _relevantSources = new List<RelevantSource>();
private string _userName { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
LoadData();
var msgs = await _localStorage.GetItemAsync<List<MessageInfo>>("msgs");
await LoadData();
}
protected override async Task OnParametersSetAsync()
{
await LoadData();
}
/// <summary>
/// 初始化加载数据
/// </summary>
/// <returns></returns>
private async Task LoadData()
{
app = _apps_Repositories.GetFirst(p => p.Id == AppId);
var userSessionStorageResult = await _protectedSessionStore.GetAsync<UserSession>("UserSession");
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
_userName = userSession?.UserName;
await GetMsgList();
}
/// <summary>
/// 获取聊天记录列表
/// </summary>
/// <returns></returns>
private async Task GetMsgList()
{
List<Chats> msgs = new List<Chats>();
if (string.IsNullOrEmpty(_userName))
{
//匿名访问使用localstore
msgs = await _localStorage.GetItemAsync<List<Chats>>($"msgs:{AppId}");
}
else
{
msgs = await _chats_Repositories.GetListAsync(p => p.AppId == AppId && p.UserName == _userName);
}
if (msgs != null && msgs.Count > 0)
{
MessageList = msgs;
}
}
protected override async Task OnParametersSetAsync()
/// <summary>
/// 清空聊天记录列表
/// </summary>
/// <returns></returns>
private async Task ClearMsgList()
{
LoadData();
MessageList.Clear();
if (string.IsNullOrEmpty(_userName))
{
await _localStorage.SetItemAsync<List<Chats>>($"msgs:{AppId}", MessageList);
}
else
{
await _chats_Repositories.DeleteAsync(p => p.AppId == AppId && p.UserName == _userName);
}
}
private void LoadData()
/// <summary>
/// 保存聊天记录
/// </summary>
/// <returns></returns>
private async Task SaveMsg(List<Chats> MessageList)
{
app = _apps_Repositories.GetFirst(p => p.Id == AppId);
if (string.IsNullOrEmpty(_userName))
{
await _localStorage.SetItemAsync<List<Chats>>($"msgs:{AppId}", MessageList);
}
else
{
if (MessageList.Count() > 0)
{
await _chats_Repositories.InsertAsync(MessageList.LastOrDefault());
}
}
}
protected async Task OnClearAsync()
@@ -78,11 +146,11 @@ namespace AntSK.Pages.ChatPage.Components
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
MessageList.Clear();
await _localStorage.SetItemAsync<List<MessageInfo>>("msgs", MessageList);
await ClearMsgList();
await InvokeAsync(StateHasChanged);
_ = Message.Info("清理成功");
}
}
else
@@ -102,18 +170,25 @@ namespace AntSK.Pages.ChatPage.Components
var filePath = fileList.FirstOrDefault()?.Url;
var fileName = fileList.FirstOrDefault()?.FileName;
MessageList.Add(new MessageInfo()
var chat = new Chats()
{
ID = Guid.NewGuid().ToString(),
Id = Guid.NewGuid().ToString(),
UserName = _userName,
AppId = AppId,
Context = _messageInput,
CreateTime = DateTime.Now,
IsSend = true
});
};
MessageList.Add(chat);
if (!string.IsNullOrEmpty(_userName))
{
await _chats_Repositories.InsertAsync(chat);
}
Sendding = true;
await SendAsync(_messageInput,filePath);
await SendAsync(_messageInput, filePath);
_messageInput = "";
Sendding = false;
Sendding = false;
}
catch (System.Exception ex)
{
@@ -123,7 +198,9 @@ namespace AntSK.Pages.ChatPage.Components
}
}
protected async Task OnCopyAsync(MessageInfo item)
protected async Task OnCopyAsync(Chats item)
{
await Task.Run(() =>
{
@@ -135,48 +212,80 @@ namespace AntSK.Pages.ChatPage.Components
{
await Task.Run(() =>
{
MessageList = MessageList.Where(w => w.ID != id).ToList();
MessageList = MessageList.Where(w => w.Id != id).ToList();
});
}
/// <summary>
/// 开始发送消息
/// </summary>
/// <param name="questions"></param>
/// <param name="filePath"></param>
/// <returns></returns>
protected async Task<bool> SendAsync(string questions, string? filePath)
{
ChatHistory history = new ChatHistory();
//处理多轮会话
Apps app = _apps_Repositories.GetFirst(p => p.Id == AppId);
if (MessageList.Count > 0)
{
history = await _chatService.GetChatHistory(MessageList);
}
ChatHistory history;
if (app.Type == AppType.chat.ToString() && (filePath == null || app.EmbeddingModelID.IsNull()))
{
await SendChat(questions, history, app);
if (string.IsNullOrEmpty(app.Prompt))
{
app.Prompt = "你叫AntSK,是一个人工智能助手";
}
//聊天应用增加系统角色
history = new ChatHistory(app.Prompt.ConvertToString());
if (MessageList.Count > 0)
{
history = await _chatService.GetChatHistory(MessageList, history);
}
await SendChat(history, app);
}
else if (app.Type == AppType.kms.ToString() || filePath != null || app.EmbeddingModelID.IsNotNull())
{
history = new ChatHistory();
if (MessageList.Count > 0)
{
history = await _chatService.GetChatHistory(MessageList, history);
}
await SendKms(questions, history, app, filePath);
}
else if (app.Type == AppType.img.ToString())
{
await SendImg(questions,app);
await SendImg(questions, app);
}
//缓存消息记录
if (app.Type != AppType.img.ToString())
{
await _localStorage.SetItemAsync<List<MessageInfo>>("msgs", MessageList);
await SaveMsg(MessageList);
if (OnRelevantSources.IsNotNull())
{
await OnRelevantSources.InvokeAsync(_relevantSources);
}
}
return await Task.FromResult(true);
}
/// <summary>
/// 发送图片对话
/// </summary>
/// <param name="questions"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendImg(string questions,Apps app)
{
MessageInfo info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
Chats info = new Chats();
info.Id = Guid.NewGuid().ToString();
info.UserName=_userName;
info.AppId=AppId;
info.CreateTime = DateTime.Now;
var base64= await _chatService.SendImgByAppAsync(app, questions);
if (string.IsNullOrEmpty(base64))
@@ -199,27 +308,23 @@ namespace AntSK.Pages.ChatPage.Components
/// <returns></returns>
private async Task SendKms(string questions, ChatHistory history, Apps app, string? filePath)
{
MessageInfo info = null;
Chats info = new Chats()
{
Id = Guid.NewGuid().ToString(),
AppId = AppId,
UserName = _userName,
CreateTime = DateTime.Now,
Context=""
};
MessageList.Add(info);
var chatResult = _chatService.SendKmsByAppAsync(app, questions, history, filePath, _relevantSources);
await foreach (var content in chatResult)
{
if (info == null)
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info.Context = content.ConvertToString();
info.CreateTime = DateTime.Now;
MessageList.Add(info);
}
else
{
info.Context += content.ConvertToString();
await Task.Delay(50);
}
info.Context += content.ConvertToString();
await Task.Delay(50);
await InvokeAsync(StateHasChanged);
}
await OnRelevantSources.InvokeAsync(_relevantSources);
//全部处理完后再处理一次Markdown
await MarkDown(info);
}
@@ -227,20 +332,21 @@ namespace AntSK.Pages.ChatPage.Components
/// <summary>
/// 发送普通对话
/// </summary>
/// <param name="questions"></param>
/// <param name="history"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendChat(string questions, ChatHistory history, Apps app)
private async Task SendChat(ChatHistory history, Apps app)
{
MessageInfo info = null;
var chatResult = _chatService.SendChatByAppAsync(app, questions, history);
Chats info = null;
var chatResult = _chatService.SendChatByAppAsync(app, history);
await foreach (var content in chatResult)
{
if (info == null)
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info = new Chats();
info.Id = Guid.NewGuid().ToString();
info.UserName = _userName;
info.AppId = AppId;
info.Context = content.ConvertToString();
info.CreateTime = DateTime.Now;
@@ -257,7 +363,12 @@ namespace AntSK.Pages.ChatPage.Components
await MarkDown(info);
}
private async Task MarkDown(MessageInfo info)
/// <summary>
/// 处理markdown
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
private async Task MarkDown(Chats info)
{
if (info.IsNotNull())
{
@@ -270,6 +381,10 @@ namespace AntSK.Pages.ChatPage.Components
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
}
/// <summary>
/// 上传文件事件
/// </summary>
/// <param name="fileInfo"></param>
private void OnSingleCompleted(UploadInfo fileInfo)
{
fileList.Add(new()
@@ -281,6 +396,11 @@ namespace AntSK.Pages.ChatPage.Components
});
_kMService.OnSingleCompleted(fileInfo);
}
/// <summary>
/// 移除文件事件
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
private async Task<bool> HandleFileRemove(UploadFileItem file)
{
fileList.RemoveAll(x => x.FileName == file.FileName);

View File

@@ -6,14 +6,16 @@
@page "/OpenChat/{AppId}"
@layout OpenLayout
<ChatView AppId="@AppId" ShowTitle=true>
</ChatView>
<style>
#chat{
height:100% ;
display:flex;
#chat {
height: 100%;
display: flex;
flex-direction: column;
overflow-x: hidden;
font-family: Arial, sans-serif;
@@ -21,7 +23,7 @@
padding: 10px;
}
</style>
@code{
@code {
[Parameter]
public string AppId { get; set; }
}

View File

@@ -70,7 +70,7 @@ namespace AntSK.Pages
private void NavToAIModel()
{
NavigationManager.NavigateTo("/setting/modellist");
NavigationManager.NavigateTo("/modelmanager/modellist");
}
}
}

View File

@@ -80,7 +80,7 @@ namespace AntSK.Pages.KmsPage
private void NavigateModelList()
{
NavigationManager.NavigateTo("/setting/modellist");
NavigationManager.NavigateTo("/modelmanager/modellist");
}
}
}

View File

@@ -5,9 +5,9 @@
@using AntSK.LLamaFactory.Model
@using AntSK.LLamaFactory
@using BlazorComponents.Terminal
@page "/setting/model/add"
@page "/setting/model/add/{ModelId}"
@page "/setting/model/addbypath/{ModelPath}"
@page "/modelmanager/model/add"
@page "/modelmanager/model/add/{ModelId}"
@page "/modelmanager/model/addbypath/{ModelPath}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@@ -27,21 +27,25 @@
<EnumRadioGroup @bind-Value="context.AIType" ButtonStyle="RadioButtonStyle.Solid" OnChange="AITypeChange" TEnum="AIType"> </EnumRadioGroup>
</FormItem>
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<RadioGroup @bind-Value="context.AIModelType">
<RadioGroup @bind-Value="context.AIModelType">
@if (context.AIType == AIType.StableDiffusion)
{
<Radio RadioButton Value="@(AIModelType.Image)">图片模型</Radio>
}
else
else
{
@if (context.AIType != AIType.BgeEmbedding)
@if (context.AIType != AIType.BgeEmbedding && context.AIType != AIType.BgeRerank)
{
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
}
@if (context.AIType != AIType.LLamaFactory && context.AIType != AIType.Mock && context.AIType != AIType.SparkDesk)
@if (context.AIType != AIType.LLamaFactory && context.AIType != AIType.Mock && context.AIType != AIType.SparkDesk && context.AIType != AIType.BgeRerank)
{
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
}
@if (context.AIType == AIType.BgeRerank)
{
<Radio RadioButton Value="@(AIModelType.Rerank)">Rerank重排模型</Radio>
}
}
</RadioGroup>
</FormItem>
@@ -81,12 +85,13 @@
<FormItem Label="APPID" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入APPID" @bind-Value="@context.EndPoint" />
</FormItem>
<FormItem Label="APIKey" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入请输入APIKey" @bind-Value="@context.ModelName" />
</FormItem>
<FormItem Label="APISecret" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<InputPassword @bind-Value="@context.ModelKey" Placeholder="APISecret" Size="@InputSize.Large" />
</FormItem>
<FormItem Label="APIKey" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入请输入APIKey" @bind-Value="@context.ModelName" />
</FormItem>
}
@if (context.AIType == AIType.DashScope)
{
@@ -140,7 +145,7 @@
<Button OnClick="StopLFService" Disabled="@(!llamaFactoryIsStart)">停止</Button>
}
</InputGroup>
</FormItem>
</FormItem>
}
@if (context.AIType == AIType.BgeEmbedding)
@@ -152,19 +157,45 @@
LabelProperty="c=>c">
</Select>
</FormItem>
<FormItem Label="PythonDll路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="D:\Programs\Python\Python311\python311.dll" @bind-Value="@context.EndPoint" />
<FormItem Label="Python路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Popover ContentTemplate="@_content" Title="提示" Style="width:100%">
<Input Placeholder="D:\Programs\Python\Python311\python311.dll" @bind-Value="@context.EndPoint" />
</Popover>
</FormItem>
<FormItem Label="环境安装" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Button Type="primary" OnClick="PipInstall">环境安装</Button>
</FormItem>
<FormItem Label="下载并初始化" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Spin Tip="请等待..." Spinning="@(BgeEmbeddingIsStart)">
<Button Type="primary" Disabled="@(BgeEmbeddingIsStart)" OnClick="BgeEmbedding">@BgeEmbeddingBtnText</Button>
</Spin>
</FormItem>
}
@if (context.AIType == AIType.BgeRerank)
{
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@bgeRerankList"
@bind-Value="@context.ModelName"
ValueProperty="c=>c"
LabelProperty="c=>c">
</Select>
</FormItem>
<FormItem Label="Python路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Popover ContentTemplate="@_content" Title="提示" Style="width:100%">
<Input Placeholder="D:\Programs\Python\Python311\python311.dll" @bind-Value="@context.EndPoint" />
</Popover>
</FormItem>
<FormItem Label="环境安装" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Button Type="primary" OnClick="PipInstall">环境安装</Button>
</FormItem>
<FormItem Label="下载并初始化" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Spin Tip="请等待..." Spinning="@(BgeIsStart)" >
<Button Type="primary" Disabled="@(BgeIsStart)" OnClick="BgeDownload">@BgeBtnText</Button>
<Spin Tip="请等待..." Spinning="@(BgeRerankIsStart)">
<Button Type="primary" Disabled="@(BgeRerankIsStart)" OnClick="BgeRerank">@BgeRerankBtnText</Button>
</Spin>
</FormItem>
}
@if (context.AIType == AIType.Mock)
{
}
@@ -206,5 +237,8 @@
@code
{
RenderFragment _content =
@<div>
<p>在windows上使用 python311.dll文件在linux上使用 libpython3.10.so</p>
</div>;
}

View File

@@ -3,7 +3,7 @@ using AntDesign.ProLayout;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Constant;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Domain.Other.Bge;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
@@ -61,8 +61,13 @@ namespace AntSK.Pages.Setting.AIModel
private bool _logModalVisible;
private List<string> bgeEmbeddingList = new List<string>() { "AI-ModelScope/bge-small-zh-v1.5", "AI-ModelScope/bge-base-zh-v1.5", "AI-ModelScope/bge-large-zh-v1.5" };
private bool BgeIsStart = false;
private string BgeBtnText = "初始化";
private List<string> bgeRerankList = new List<string>() { "Xorbits/bge-reranker-base", "Xorbits/bge-reranker-large", "AI-ModelScope/bge-reranker-v2-m3", "AI-ModelScope/bge-reranker-v2-gemma"};
private bool BgeEmbeddingIsStart = false;
private string BgeEmbeddingBtnText = "初始化";
private bool BgeRerankIsStart = false;
private string BgeRerankBtnText = "初始化";
protected override async Task OnInitializedAsync()
{
@@ -150,7 +155,7 @@ namespace AntSK.Pages.Setting.AIModel
private void Back()
{
NavigationManager.NavigateTo("/setting/modellist");
NavigationManager.NavigateTo("/modelmanager/modellist");
}
private async Task StartDownload()
@@ -268,7 +273,7 @@ namespace AntSK.Pages.Setting.AIModel
}
}
private async Task BgeDownload()
private async Task BgeEmbedding()
{
if (string.IsNullOrEmpty(_aiModel.ModelName))
{
@@ -277,27 +282,60 @@ namespace AntSK.Pages.Setting.AIModel
}
if (string.IsNullOrEmpty(_aiModel.EndPoint))
{
_ = Message.Error("请输入正确的Python dll路径", 2);
_ = Message.Error("请输入正确的Python dll或python so路径!", 2);
return;
}
BgeIsStart = true;
BgeBtnText = "正在初始化...";
BgeEmbeddingIsStart = true;
BgeEmbeddingBtnText = "正在初始化...";
await Task.Run(() =>
{
try
{
EmbeddingConfig.LoadModel(_aiModel.EndPoint, _aiModel.ModelName);
BgeBtnText = "初始化完成";
BgeIsStart = false;
BgeEmbeddingConfig.LoadModel(_aiModel.EndPoint, _aiModel.ModelName);
BgeEmbeddingBtnText = "初始化完成";
BgeEmbeddingIsStart = false;
}
catch (System.Exception ex)
{
_ = Message.Error(ex.Message, 2);
BgeIsStart = false;
BgeEmbeddingIsStart = false;
}
});
}
private async Task BgeRerank()
{
if (string.IsNullOrEmpty(_aiModel.ModelName))
{
_ = Message.Error("请输入模型名称!", 2);
return;
}
if (string.IsNullOrEmpty(_aiModel.EndPoint))
{
_ = Message.Error("请输入正确的Python dll或python so路径", 2);
return;
}
BgeRerankIsStart = true;
BgeRerankBtnText = "正在初始化...";
await Task.Run(() =>
{
try
{
BegRerankConfig.LoadModel(_aiModel.EndPoint, _aiModel.ModelName);
BgeRerankBtnText = "初始化完成";
BgeRerankIsStart = false;
}
catch (System.Exception ex)
{
_ = Message.Error(ex.Message, 2);
BgeRerankIsStart = false;
}
});
}
private async Task CmdLogHandler(string message)
{
await InvokeAsync(() =>
@@ -331,6 +369,9 @@ namespace AntSK.Pages.Setting.AIModel
case AIType.BgeEmbedding:
_aiModel.AIModelType = AIModelType.Embedding;
break;
case AIType.BgeRerank:
_aiModel.AIModelType = AIModelType.Rerank;
break;
default:
_aiModel.AIModelType = AIModelType.Chat;
break;

View File

@@ -1,69 +1,69 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/modeldown"
@page "/modelmanager/modeldown"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@using AntSK.Domain.Domain.Model.hfmirror;
using AntSK.Domain.Domain.Model.hfmirror
@using AntSK.Domain.Domain.Model.hfmirror
@attribute [Authorize(Roles = "AntSKAdmin")]
<div>
<PageContainer Title="模型列表">
<Content>
<RadioGroup @bind-Value="@_modelType" OnChange="OnModelTypeChange" TValue="string">
<Radio Value="@("gguf")" DefaultChecked=true>LLama本地模型(gguf)</Radio>
<Radio Value="@("safetensors")">StableDiffusion(safetensors)</Radio>
<Radio Value="@("ckpt")">StableDiffusion2(ckpt)</Radio>
</RadioGroup>
<div style="text-align: center;">
<PageContainer Title="模型列表">
<Content>
<RadioGroup @bind-Value="@_modelType" OnChange="OnModelTypeChange" TValue="string">
<Radio Value="@("gguf")" DefaultChecked=true>LLama本地模型(gguf)</Radio>
<Radio Value="@("safetensors")">StableDiffusion(safetensors)</Radio>
<Radio Value="@("ckpt")">StableDiffusion2(ckpt)</Radio>
</RadioGroup>
<div style="text-align: center;">
<Search Placeholder="输入回车"
EnterButton="@("搜索")"
Size="large"
Style="max-width: 522px; width: 100%;"
OnSearch="Search" />
</div>
</Content>
<ChildContent>
<div class="filterCardList">
<Spin Tip="加载中..." Spinning="@(loaddding)">
<AntList TItem="HfModels"
Grid="LayoutModel._listGridType"
DataSource="_modelList">
<ListItem NoFlex>
<Card Hoverable
BodyStyle="padding-bottom: 20px;"
Actions="new[] {
down(()=> Down(context.Id))
<Search Placeholder="输入回车"
EnterButton="@("搜索")"
Size="large"
Style="max-width: 522px; width: 100%;"
OnSearch="Search" />
</div>
</Content>
<ChildContent>
<div class="filterCardList">
<Spin Tip="加载中..." Spinning="@(loaddding)">
<AntList TItem="HfModels"
Grid="LayoutModel._listGridType"
DataSource="_modelList">
<ListItem NoFlex>
<Card Hoverable
BodyStyle="padding-bottom: 20px;"
Actions="new[] {
down(()=> Down(context.Id))
}">
<CardMeta>
<TitleTemplate>
@context.Id
</TitleTemplate>
<AvatarTemplate>
<Avatar Size="small" Src="@context.AuthorData.AvatarUrl" />
</AvatarTemplate>
</CardMeta>
<div class="cardItemContent">
<div class="cardInfo">
<div>
<p>Downloads</p>
<p>@context.Downloads.ToString("0,0")</p>
<CardMeta>
<TitleTemplate>
@context.Id
</TitleTemplate>
<AvatarTemplate>
<Avatar Size="small" Src="@context.AuthorData.AvatarUrl" />
</AvatarTemplate>
</CardMeta>
<div class="cardItemContent">
<div class="cardInfo">
<div>
<p>Downloads</p>
<p>@context.Downloads.ToString("0,0")</p>
</div>
<div>
<p>Likes</p>
<p>@context.Likes.ToString("0,0")</p>
</div>
</div>
</div>
<div>
<p>Likes</p>
<p>@context.Likes.ToString("0,0")</p>
</div>
</div>
</div>
</Card>
</ListItem>
</AntList>
</Spin>
</div>
</ChildContent>
</PageContainer>
</Card>
</ListItem>
</AntList>
</Spin>
</div>
</ChildContent>
</PageContainer>
</div>
@code
{
RenderFragment down(Action clickAction) =>@<a key="down" @onclick="@clickAction">下载</a>;

View File

@@ -71,7 +71,7 @@ namespace AntSK.Pages.Setting.AIModel
private void Down(string modelPath)
{
NavigationManager.NavigateTo($"/setting/modeldown/detail/{modelPath}");
NavigationManager.NavigateTo($"/modelmanager/modeldown/detail/{modelPath}");
}
private void OnModelTypeChange(string value)

View File

@@ -1,5 +1,5 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/modeldown/detail/{ModelName}/{ModelPath}"
@page "/modelmanager/modeldown/detail/{ModelName}/{ModelPath}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization

View File

@@ -50,7 +50,7 @@ namespace AntSK.Pages.Setting.AIModel
private void Down(string path)
{
NavigationManager.NavigateTo($"/setting/model/addbypath/{path.Replace("?download=true", "").Replace("/", "---")}");
NavigationManager.NavigateTo($"/modelmanager/model/addbypath/{path.Replace("?download=true", "").Replace("/", "---")}");
}
}
}

View File

@@ -1,7 +1,7 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Domain.Domain.Model.Enum
@page "/setting/modellist"
@page "/modelmanager/modellist"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
@@ -39,44 +39,48 @@
delete(async ()=>await Delete(context.Id))
}" Style="width:100%">
<div class="listContent" style="width:100%">
<div class="listContentItem" style="width:20%">
<div class="listContentItem" style="width:15%">
<b>模型描述</b>
<p>@context.ModelDescription</p>
<p title="@context.ModelDescription">@context.ModelDescription</p>
</div>
<div class="listContentItem" style="width:10%">
<b>AI类型</b>
<p>
@if (context.AIType == AIType.OpenAI)
{
<Tag Color="@PresetColor.Yellow.ToString()">OpenAI</Tag>
<Tag Color="@PresetColor.Pink.ToString()">OpenAI</Tag>
}
else if (context.AIType == AIType.AzureOpenAI)
{
<Tag Color="@PresetColor.Green.ToString()">AzureOpenAI</Tag>
<Tag Color="@PresetColor.Red.ToString()">AzureOpenAI</Tag>
}
else if (context.AIType == AIType.LLamaSharp)
{
<Tag Color="@PresetColor.Red.ToString()">LLamaSharp</Tag>
<Tag Color="@PresetColor.Yellow.ToString()">LLamaSharp</Tag>
}
else if (context.AIType == AIType.SparkDesk)
{
<Tag Color="@PresetColor.Orange.ToString()">SparkDesk</Tag>
<Tag Color="@PresetColor.Cyan.ToString()">SparkDesk</Tag>
}
else if (context.AIType == AIType.Mock)
{
<Tag Color="@PresetColor.Cyan.ToString()">Mock</Tag>
<Tag Color="@PresetColor.Green.ToString()">Mock</Tag>
}
else if (context.AIType == AIType.LLamaFactory)
{
<Tag Color="@PresetColor.Cyan.ToString()">LLamaFactory</Tag>
<Tag Color="@PresetColor.Blue.ToString()">LLamaFactory</Tag>
}
else if (context.AIType == AIType.BgeEmbedding)
{
<Tag Color="@PresetColor.Gold.ToString()">BgeEmbedding</Tag>
<Tag Color="@PresetColor.Purple.ToString()">BgeEmbedding</Tag>
}
else if (context.AIType == AIType.BgeRerank)
{
<Tag Color="@PresetColor.GeekBlue.ToString()">BgeRerank</Tag>
}
else if (context.AIType == AIType.StableDiffusion)
{
<Tag Color="@PresetColor.Lime.ToString()">StableDiffusion</Tag>
<Tag Color="@PresetColor.Magenta.ToString()">StableDiffusion</Tag>
}
</p>
</div>
@@ -97,15 +101,19 @@
{
<Tag Color="@PresetColor.Lime.ToString()">图片模型</Tag>
}
else if (context.AIModelType == AIModelType.Rerank)
{
<Tag Color="@PresetColor.GeekBlue.ToString()">Rerank重排模型</Tag>
}
</p>
</div>
<div class="listContentItem" style="width:20%">
<div class="listContentItem" style="width:25%">
<b>模型地址</b>
<p>@context.EndPoint</p>
<p title="@context.EndPoint">@context.EndPoint</p>
</div>
<div class="listContentItem" style="width:10%">
<div class="listContentItem" style="width:15%">
<b>模型名称</b>
<p>@context.ModelName</p>
<p title="@context.ModelName">@context.ModelName</p>
</div>
</div>
</ListItem>
@@ -116,7 +124,31 @@
</ChildContent>
</PageContainer>
</div>
<style>
.listContentItem {
width: 20%;
overflow: hidden; /* 防止内容溢出 */
text-overflow: ellipsis; /* 超出部分显示省略号 */
white-space: nowrap; /* 防止内容换行 */
}
/* 为需要显示工具提示的元素设置title属性 */
.listContentItem p[title] {
cursor: pointer; /* 鼠标移动上去时显示小手势 */
}
/* 悬停样式 */
.listContentItem:hover p[title]:before {
content: attr(title); /* 使用title属性的内容 */
position: absolute;
background: #333; /* 工具提示的背景颜色 */
color: #fff; /* 工具提示的文字颜色 */
padding: 4px 8px; /* 工具提示内部的填充 */
border-radius: 4px; /* 工具提示的边框圆角 */
text-align: center; /* 工具提示文字居中对齐 */
z-index: 1000; /* 工具提示在最顶层 */
}
</style>
@code
{
RenderFragment edit(Action clickAction) =>@<a key="edit" @onclick="@clickAction">修改</a>;

View File

@@ -16,6 +16,13 @@ namespace AntSK.Pages.Setting.AIModel
[Inject]
protected IAIModels_Repositories _aIModels_Repositories { get; set; }
[Inject]
protected IApps_Repositories _apps_Repositories{ get; set; }
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject] protected MessageService? Message { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
@@ -44,12 +51,12 @@ namespace AntSK.Pages.Setting.AIModel
public async Task AddModel()
{
NavigationManager.NavigateTo("/setting/model/add");
NavigationManager.NavigateTo("/modelmanager/model/add");
}
public void Edit(string modelid)
{
NavigationManager.NavigateTo("/setting/model/add/" + modelid);
NavigationManager.NavigateTo("/modelmanager/model/add/" + modelid);
}
public async Task Delete(string modelid)
@@ -59,6 +66,16 @@ namespace AntSK.Pages.Setting.AIModel
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
if (_apps_Repositories.IsAny(p => p.ChatModelID == modelid || p.EmbeddingModelID == modelid))
{
_ = Message.Error("该模型有应用在使用,请先删除应用后才允许删除该模型");
return;
}
if (_kmss_Repositories.IsAny(p => p.ChatModelID == modelid || p.EmbeddingModelID == modelid))
{
_ = Message.Error("该模型有知识库在使用,请先删除知识库后才允许删除该模型");
return;
}
await _aIModels_Repositories.DeleteAsync(modelid);
await InitData("");
}

View File

@@ -0,0 +1,121 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/chathistory"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@using System.ComponentModel
@using AntDesign.TableModels
@using AntSK.Domain.Repositories
@using AntSK.Domain.Common.Map
@attribute [Authorize(Roles = "AntSKAdmin")]
<Table @ref="table"
TItem="ChatsDto"
DataSource="@chatDtoList"
Total="_total"
@bind-PageIndex="_pageIndex"
@bind-PageSize="_pageSize"
OnChange="OnChange"
Size="TableSize.Small"
RowKey="x=>x.Id">
<TitleTemplate>
<GridRow>
<GridCol Span="4">
<Title Level="3">聊天记录</Title>
</GridCol>
<GridCol Span="8" Offset="12">
<Search Placeholder="搜索" @bind-Value="searchString" OnSearch="Search" />
</GridCol>
</GridRow>
</TitleTemplate>
<ColumnDefinitions>
<PropertyColumn Property="c=>c.Id" />
<PropertyColumn Title="用户名" Property="c=>c.UserName" />
<PropertyColumn Title="应用名称" Property="c=>c.AppName" />
<PropertyColumn Title="发送/接收" Property="c=>c.SendReveice" />
<ActionColumn Title="消息内容" Width="30%">
<Space>
<SpaceItem>
@((MarkupString)(context.Context))
</SpaceItem>
</Space>
</ActionColumn>
<PropertyColumn Title="时间" Property="c=>c.CreateTime" Format="yyyy-MM-dd HH:mm:ss" />
</ColumnDefinitions>
</Table>
@using System.Text.Json;
@code {
[Inject] IChats_Repositories _chats_Repositories { get; set; }
ChatsDto[] chatDtoList;
ITable table;
int _pageIndex = 1;
int _pageSize = 10;
int _total = 0;
string searchString;
protected override async Task OnInitializedAsync()
{
await InitData();
}
public async Task OnChange(QueryModel<ChatsDto> queryModel)
{
await InitData();
}
private async Task InitData()
{
if (string.IsNullOrEmpty(searchString))
{
_total = _chats_Repositories.Count(p => true);
var chatList = _chats_Repositories.GetDB().Queryable<Chats, Apps>((c, a) => new object[] {
SqlSugar.JoinType.Left,c.AppId==a.Id
}).Select((c, a) => new ChatsDto
{
Id = c.Id,
UserName = c.UserName,
AppId = c.AppId,
IsSend = c.IsSend,
SendReveice = c.IsSend ? "发送" : "接收",
Context = c.Context,
CreateTime = c.CreateTime,
AppName = a.Name
}).ToPageList(_pageIndex, _pageSize);
chatDtoList = chatList.ToArray();
}
else
{
_total = _chats_Repositories.Count(p => p.UserName.Contains(searchString) || p.Context.Contains(searchString));
var chatList = _chats_Repositories.GetDB().Queryable<Chats, Apps>((c, a) => new object[] {
SqlSugar.JoinType.Left,c.AppId==a.Id
}).Select((c, a) => new ChatsDto
{
Id = c.Id,
UserName = c.UserName,
AppId = c.AppId,
IsSend = c.IsSend,
SendReveice = c.IsSend ? "发送" : "接收",
Context = c.Context,
CreateTime = c.CreateTime,
AppName = a.Name
}).Where(c => c.UserName.Contains(searchString) || c.Context.Contains(searchString)).ToPageList(_pageIndex, _pageSize);
chatDtoList = chatList.ToArray();
}
}
private async Task Search(string searchKey)
{
await InitData();
}
public class ChatsDto : Chats
{
public string AppName { get; set; }
public string SendReveice { get; set; }
}
}

View File

@@ -0,0 +1,89 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/delkms"
@using AntSK.Services.Auth
@using AntSK.Domain.Options
@using AntSK.Domain.Repositories
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "AntSKAdmin")]
<GridContent>
<Card>
<Result Status="error"
Title="删除向量表"
SubTitle="删除向量表将会删除您的所有知识库文件,以及知识库向量。请谨慎操作!"
Style="margin-top: 48px; margin-bottom: 16px;">
<Extra>
<Button Type="primary" OnClick="DelDimensions">删除向量表</Button>
</Extra>
<ChildContent>
<div class="title__b__0">
当您切换不同维度的向量模型时,出现异常:
</div>
<div style="margin-bottom: 16px;">
<Icon Type="close-circle" Theme="outline" Class="error_icon" Style="margin-right: 8px;" />
Npgsql.PostgresException (0x80004005): 22000: expected 2048 dimensions, not 1024
</div>
<div>
<Icon Type="close-circle" Theme="outline" Class="error_icon" Style="margin-right: 8px;" />
您需要删除向量表,然后重建向量维度。
</div>
</ChildContent>
</Result>
</Card>
</GridContent>
<Modal @ref="_modal" Visible="_showAdminPwdVisible" Footer="null" Closable Title="请输入管理员密码" OnCancel="OnCancel" DestroyOnClose>
<Flex Gap="10" Vertical>
<InputGroup>
<InputPassword @bind-Value="@adminPwd" Placeholder="管理员密码" Size="@InputSize.Large" />
</InputGroup>
<InputGroup>
<Button Type="primary" OnClick="DelSubmit">确认</Button>
</InputGroup>
</Flex>
</Modal>
@code {
[Inject] protected MessageService? Message { get; set; }
[Inject] protected IKmsDetails_Repositories kmsDetails_Repositories { get; set; }
[Inject] protected IConfirmService _confirmService { get; set; }
private Modal _modal;
private bool _showAdminPwdVisible = false;
private string adminPwd = "";
public async Task DelDimensions()
{
var content = "删除向量表将会删除您的所有知识库文件,以及知识库向量。请谨慎操作!";
var title = "删除向量表";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
_showAdminPwdVisible = true;
}
}
public async Task DelSubmit() {
if (adminPwd != LoginOption.Password)
{
_ = Message.Error("管理员密码错误!");
return;
}
kmsDetails_Repositories.GetDB().Ado.ExecuteCommand("DROP TABLE IF EXISTS \"km-kms\"");
kmsDetails_Repositories.GetDB().Ado.ExecuteCommand("DROP TABLE IF EXISTS \"km-file\"");
var kmsDetails = await kmsDetails_Repositories.GetListAsync();
foreach (var detail in kmsDetails)
{
kmsDetails_Repositories.Delete(detail);
}
_showAdminPwdVisible = false;
_ = Message.Info("删除成功!");
}
private void OnCancel()
{
_showAdminPwdVisible = false;
}
}

View File

@@ -65,13 +65,19 @@ builder.Services.AddBackgroundTaskBroker().AddHandler<ImportKMSTaskReq, BackGrou
{
NativeLibraryConfig
.Instance
.WithCuda(false);
.WithCuda(false)
.WithLogCallback((level, message) => {
Console.WriteLine($"[llama {level}]: {message.TrimEnd('\n')}");
});
}
else if (LLamaSharpOption.RunType.ToUpper() == "GPU")
{
NativeLibraryConfig
.Instance
.WithCuda(true)
.WithLogCallback((level, message) => {
Console.WriteLine($"[llama {level}]: {message.TrimEnd('\n')}");
})
.WithAvx(NativeLibraryConfig.AvxLevel.Avx);
}
}

View File

@@ -41,13 +41,14 @@ namespace AntSK.Services.OpenApi
{
case "chat":
//普通会话
history.AddUserMessage(questions);
if (model.stream)
{
OpenAIStreamResult result1 = new OpenAIStreamResult();
result1.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result1.choices = new List<StreamChoicesModel>()
{ new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
await SendChatStream(HttpContext, result1, app, questions,history);
await SendChatStream(HttpContext, result1, app,history);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result1));
await HttpContext.Response.CompleteAsync();
@@ -59,14 +60,12 @@ namespace AntSK.Services.OpenApi
result2.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result2.choices = new List<ChoicesModel>()
{ new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result2.choices[0].message.content = await SendChat(questions,history, app);
result2.choices[0].message.content = await SendChat(history, app);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result2));
await HttpContext.Response.CompleteAsync();
}
break;
case "kms":
//知识库问答
if (model.stream)
@@ -91,16 +90,15 @@ namespace AntSK.Services.OpenApi
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result4));
await HttpContext.Response.CompleteAsync();
}
break;
}
}
}
private async Task SendChatStream(HttpContext HttpContext, OpenAIStreamResult result, Apps app,string questions, ChatHistory history)
private async Task SendChatStream(HttpContext HttpContext, OpenAIStreamResult result, Apps app, ChatHistory history)
{
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
var chatResult = _chatService.SendChatByAppAsync(app, questions, history);
var chatResult = _chatService.SendChatByAppAsync(app, history);
await foreach (var content in chatResult)
{
result.choices[0].delta.content = content.ConvertToString();
@@ -113,7 +111,6 @@ namespace AntSK.Services.OpenApi
await HttpContext.Response.WriteAsync("data: [DONE]");
await HttpContext.Response.Body.FlushAsync();
await HttpContext.Response.CompleteAsync();
}
@@ -124,49 +121,48 @@ namespace AntSK.Services.OpenApi
/// <param name="history"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task<string> SendChat(string questions, ChatHistory history, Apps app)
private async Task<string> SendChat(ChatHistory history, Apps app)
{
string result = "";
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
{
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
}
KernelArguments args = new KernelArguments();
if (history.Count > 10)
{
app.Prompt = @"${{ConversationSummaryPlugin.SummarizeConversation $history}}" + app.Prompt;
args = new() {
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) },
{ "input", questions }
};
}
else
{
args = new()
{
{ "input", $"{string.Join("\n", history.Select(x => x.Role + ": " + x.Content))}{Environment.NewLine} user:{questions}" }
};
}
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100; //存的是0~100需要缩小
var chat = _kernel.GetRequiredService<IChatCompletionService>();
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
List<string> completionList = new List<string>();
if (!string.IsNullOrEmpty(app.ApiFunctionList) || !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
}
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
var chatResult =await _kernel.InvokeAsync(function: func, arguments: args);
if (chatResult.IsNotNull())
{
string answers = chatResult.GetValue<string>();
result = answers;
}
settings.ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions;
while (true)
{
ChatMessageContent result = await chat.GetChatMessageContentAsync(history, settings, _kernel);
if (result.Content is not null)
{
string chunkCompletion = result.Content.ConvertToString();
completionList.Add(chunkCompletion);
return chunkCompletion;
}
history.Add(result);
IEnumerable<FunctionCallContent> functionCalls = FunctionCallContent.GetFunctionCalls(result);
if (!functionCalls.Any())
{
break;
}
return result;
foreach (var functionCall in functionCalls)
{
FunctionResultContent resultContent = await functionCall.InvokeAsync(_kernel);
history.Add(resultContent.ToChatMessage());
}
}
}
else
{
ChatMessageContent result = await chat.GetChatMessageContentAsync(history, settings, _kernel);
return result.Content.ConvertToString();
}
return "";
}
private async Task SendKmsStream(HttpContext HttpContext, OpenAIStreamResult result, Apps app, string questions,ChatHistory history)

View File

@@ -1 +1,30 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" version="1.1" viewBox="0 0 200 200"><title>Group 28 Copy 5</title><desc>Created with Sketch.</desc><defs><linearGradient id="linearGradient-1" x1="62.102%" x2="108.197%" y1="0%" y2="37.864%"><stop offset="0%" stop-color="#4285EB"/><stop offset="100%" stop-color="#2EC7FF"/></linearGradient><linearGradient id="linearGradient-2" x1="69.644%" x2="54.043%" y1="0%" y2="108.457%"><stop offset="0%" stop-color="#29CDFF"/><stop offset="37.86%" stop-color="#148EFF"/><stop offset="100%" stop-color="#0A60FF"/></linearGradient><linearGradient id="linearGradient-3" x1="69.691%" x2="16.723%" y1="-12.974%" y2="117.391%"><stop offset="0%" stop-color="#FA816E"/><stop offset="41.473%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient><linearGradient id="linearGradient-4" x1="68.128%" x2="30.44%" y1="-35.691%" y2="114.943%"><stop offset="0%" stop-color="#FA8E7D"/><stop offset="51.264%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient></defs><g id="Page-1" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="logo" transform="translate(-20.000000, -20.000000)"><g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)"><g id="Group-27-Copy-3"><g id="Group-25" fill-rule="nonzero"><g id="2"><path id="Shape" fill="url(#linearGradient-1)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/><path id="Shape" fill="url(#linearGradient-2)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/></g><path id="Shape" fill="url(#linearGradient-3)" d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z"/></g><ellipse id="Combined-Shape" cx="100.519" cy="100.437" fill="url(#linearGradient-4)" rx="23.6" ry="23.581"/></g></g></g></g></svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="370.000000pt" height="370.000000pt" viewBox="0 0 370.000000 370.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,370.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1914 3198 c-87 -152 -450 -815 -526 -960 l-46 -86 26 -7 c15 -4 116
-3 225 1 l197 7 69 121 c38 66 83 150 101 185 l32 65 19 -25 c10 -13 44 -71
75 -129 32 -58 73 -134 92 -168 l35 -64 213 4 c117 1 215 6 219 9 8 9 -57 139
-203 404 -177 324 -439 785 -446 785 0 0 -38 -64 -82 -142z"/>
<path d="M772 3019 c-198 -53 -353 -165 -459 -332 -13 -20 -41 -72 -63 -115
-51 -102 -64 -184 -58 -372 5 -129 8 -153 33 -215 48 -118 105 -205 195 -295
75 -75 101 -93 215 -152 l130 -66 440 -11 c242 -7 467 -16 499 -22 119 -21
211 -93 263 -204 24 -51 28 -72 28 -145 -2 -190 -105 -314 -305 -365 l-45 -11
-3 -212 -2 -212 22 0 c63 1 202 43 298 91 93 45 116 63 201 148 86 87 102 109
153 211 65 131 76 182 76 345 -1 248 -46 365 -209 537 -80 85 -157 141 -277
201 l-91 46 -494 11 -494 11 -40 26 c-76 50 -142 121 -171 186 -25 54 -29 74
-29 152 1 76 5 99 27 147 47 101 142 175 263 205 l65 16 0 208 0 209 -47 -1
c-27 0 -81 -9 -121 -20z"/>
<path d="M830 1153 c-38 -7 -42 -14 -123 -156 -153 -271 -169 -304 -151 -311
8 -3 109 -6 224 -6 l209 0 70 128 c132 242 176 330 168 339 -9 9 -356 14 -397
6z"/>
<path d="M2802 1152 l-42 -7 93 -170 c121 -220 154 -273 175 -279 55 -15 114
-17 263 -11 l166 7 -119 222 c-66 121 -122 224 -126 227 -10 10 -359 18 -410
11z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -35,9 +35,27 @@
}
]
},
{
"path": "/modelmanager",
"name": "模型管理",
"key": "modelmanager",
"icon": "shop",
"children": [
{
"path": "/modelmanager/modellist",
"name": "模型管理",
"key": "modelmanager.modellist"
},
{
"path": "/modelmanager/modeldown",
"name": "模型下载",
"key": "modelmanager.modeldown"
}
]
},
{
"path": "/setting",
"name": "设置",
"name": "系统设置",
"key": "setting",
"icon": "setting",
"children": [
@@ -47,21 +65,27 @@
"key": "setting.user"
},
{
"path": "/setting/modellist",
"name": "模型管理",
"key": "setting.modellist"
"path": "/setting/chathistory",
"name": "聊天记录",
"key": "setting.chathistory"
},
{
"path": "/setting/modeldown",
"name": "模型下载",
"key": "setting.modeldown"
"path": "/setting/delkms",
"name": "删除向量表",
"key": "setting.delkms"
}
]
},
{
"path": "http://antsk.cn/",
"name": "文档",
"name": "使用文档",
"key": "antskdoc",
"icon": "question-circle"
},
{
"path": "https://www.bilibili.com/video/BV1vK421b7NF",
"name": "教程视频",
"key": "antskvideo",
"icon": "video-camera"
}
]

View File

@@ -0,0 +1,55 @@
using AntSK.LLM.SparkDesk;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel;
using Sdcb.SparkDesk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text.Unicode;
using System.Threading.Tasks;
namespace AntSK.LLM.Mock
{
public class MockChatCompletion : IChatCompletionService
{
private readonly Dictionary<string, object?> _attributes = new();
private readonly SparkDeskClient _client;
private string _chatId;
private readonly SparkDeskOptions _options;
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
NumberHandling = JsonNumberHandling.AllowReadingFromString,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
public IReadOnlyDictionary<string, object?> Attributes => _attributes;
public MockChatCompletion()
{
}
public async Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
StringBuilder sb = new();
string result = $"这是一条Mock数据便于聊天测试你的消息是{chatHistory.LastOrDefault().ToString()}";
return [new(AuthorRole.Assistant, result.ToString())];
}
public async IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
StringBuilder sb = new();
string result = $"这是一条Mock数据便于聊天测试你的消息是{chatHistory.LastOrDefault().ToString()}";
foreach (var c in result)
{
yield return new StreamingChatMessageContent(AuthorRole.Assistant, c.ToString());
}
}
}
}

View File

@@ -0,0 +1,231 @@
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel;
using Sdcb.SparkDesk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text.Unicode;
using System.Threading.Tasks;
namespace AntSK.LLM.SparkDesk
{
public class SparkDeskChatCompletion : IChatCompletionService
{
private readonly Dictionary<string, object?> _attributes = new();
private readonly SparkDeskClient _client;
private string _chatId;
private readonly SparkDeskOptions _options;
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
NumberHandling = JsonNumberHandling.AllowReadingFromString,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
public IReadOnlyDictionary<string, object?> Attributes => _attributes;
public SparkDeskChatCompletion(SparkDeskOptions options, string chatId)
{
_options = options;
_chatId = chatId;
_client = new(options.AppId, options.ApiKey, options.ApiSecret);
}
public async Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
{
StringBuilder sb = new();
var parameters = new ChatRequestParameters
{
ChatId = _chatId,
};
OpenAIPromptExecutionSettings chatExecutionSettings = OpenAIPromptExecutionSettings.FromExecutionSettings(executionSettings);
parameters.Temperature = (float)chatExecutionSettings.Temperature;
parameters.MaxTokens = chatExecutionSettings.MaxTokens ?? parameters.MaxTokens;
IList<KernelFunctionMetadata> functions = kernel?.Plugins.GetFunctionsMetadata().Where(x => x.PluginName == "AntSkFunctions").ToList() ?? [];
var functionDefs = functions.Select(func => new FunctionDef(func.Name, func.Description, func.Parameters.Select(p => new FunctionParametersDef(p.Name, p.ParameterType?.IsClass == true ? "object" : "string", p.Description, p.IsRequired)).ToList())).ToList();
List<ChatMessage> messages = GetSparkMessage(chatHistory);
var result = await _client.ChatAsync(_options.ModelVersion, messages.ToArray(), parameters, functionDefs.Count > 0 ? [.. functionDefs] : null, cancellationToken: cancellationToken);
if (result.FunctionCall != null)
{
var func = functions.Where(x => x.Name == result.FunctionCall.Name).FirstOrDefault();
if (func == null)
{
return new List<ChatMessageContent> { new(AuthorRole.Assistant, $"插件{result.FunctionCall.Name}未注册") }.AsReadOnly();
}
if (kernel.Plugins.TryGetFunction(func.PluginName, func.Name, out var function))
{
var arguments = new KernelArguments();
var JsonElement = JsonDocument.Parse(result.FunctionCall.Arguments).RootElement;
foreach (var parameter in func.Parameters)
{
var error = "";
try
{
if (JsonElement.TryGetProperty(parameter.Name, out var property))
{
arguments.Add(parameter.Name, property.Deserialize(parameter.ParameterType!, _jsonSerializerOptions));
}
}
catch (Exception ex)
{
error = $"参数{parameter.Name}解析错误:{ex.Message}";
}
if (!string.IsNullOrEmpty(error))
{
return new List<ChatMessageContent> { new(AuthorRole.Assistant, error) }.AsReadOnly();
}
}
var functionResult = await function.InvokeAsync(kernel, arguments, cancellationToken);
messages = [ ChatMessage.FromUser(messages.LastOrDefault().Content),
ChatMessage.FromSystem($@"
执行函数调用成功
函数描述:{func.Description}
函数执行结果:{functionResult}
"),
ChatMessage.FromUser("请根据函数调用结果回答我的问题,不要超出函数调用结果的返回,以及不要有多余描述:")];
var callResult = await _client.ChatAsync(_options.ModelVersion, messages.ToArray(), parameters, null);
ChatMessageContent chatMessageContent = new(AuthorRole.Assistant, callResult.Text.ToString(), modelId: "SparkDesk");
return new List<ChatMessageContent> { chatMessageContent }.AsReadOnly();
}
return new List<ChatMessageContent> { new(AuthorRole.Assistant, "未找到插件") }.AsReadOnly();
}
else
{
ChatMessageContent chatMessageContent = new(AuthorRole.Assistant, result.Text.ToString(), modelId: "SparkDesk");
return new List<ChatMessageContent> { chatMessageContent }.AsReadOnly();
}
}
public async IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var parameters = new ChatRequestParameters
{
ChatId = _chatId,
};
OpenAIPromptExecutionSettings chatExecutionSettings = OpenAIPromptExecutionSettings.FromExecutionSettings(executionSettings);
parameters.Temperature = (float)chatExecutionSettings.Temperature;
parameters.MaxTokens = chatExecutionSettings.MaxTokens ?? parameters.MaxTokens;
IList<KernelFunctionMetadata> functions = kernel?.Plugins.GetFunctionsMetadata().Where(x => x.PluginName == "AntSkFunctions").ToList() ?? [];
var functionDefs = functions.Select(func => new FunctionDef(func.Name, func.Description, func.Parameters.Select(p => new FunctionParametersDef(p.Name, p.ParameterType?.IsClass == true ? "object" : "string", p.Description, p.IsRequired)).ToList())).ToList();
List<ChatMessage> messages = GetSparkMessage(chatHistory);
await foreach (StreamedChatResponse msg in _client.ChatAsStreamAsync(_options.ModelVersion, messages.ToArray(), parameters, functionDefs.Count > 0 ? [.. functionDefs] : null, cancellationToken: cancellationToken))
{
yield return new StreamingChatMessageContent(AuthorRole.Assistant, msg);
};
}
private static List<ChatMessage> GetSparkMessage(ChatHistory chatHistory)
{
List<ChatMessage> messages = new List<ChatMessage>();
foreach (var msg in chatHistory.ToList())
{
string role = "";
if (msg.Role == AuthorRole.User)
{
role = "user";
}
else if (msg.Role == AuthorRole.System)
{
role = "system";
}
else
{
role = "assistant";
}
messages.Add(new ChatMessage(role, msg.ToString()));
}
return messages;
}
private static string? ProcessFunctionResult(object functionResult, ToolCallBehavior? toolCallBehavior)
{
if (functionResult is string stringResult)
{
return stringResult;
}
if (functionResult is ChatMessageContent chatMessageContent)
{
return chatMessageContent.ToString();
}
return JsonSerializer.Serialize(functionResult, _jsonSerializerOptions);
}
public static Dictionary<string, object> ParseJsonElement(JsonElement element, string propertyName)
{
Dictionary<string, object> dict = new();
switch (element.ValueKind)
{
case JsonValueKind.Object:
foreach (JsonProperty property in element.EnumerateObject())
{
dict.Add(property.Name, ParseJsonElement(property.Value, property.Name));
}
break;
case JsonValueKind.Array:
List<object> list = new List<object>();
foreach (JsonElement arrayElement in element.EnumerateArray())
{
list.Add(ParseJsonElement(arrayElement, ""));
}
dict.Add(propertyName, list);
break;
case JsonValueKind.String:
dict.Add(propertyName, element.GetString());
break;
case JsonValueKind.Number:
dict.Add(propertyName, element.GetInt32());
break;
case JsonValueKind.True:
case JsonValueKind.False:
dict.Add(propertyName, element.GetBoolean());
break;
default:
dict.Add(propertyName, "Unsupported value type");
break;
}
return dict;
}
}
}