Compare commits

..

23 Commits
0.1.5 ... 0.1.6

Author SHA1 Message Date
zyxucp
bee2c56382 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-08 22:29:55 +08:00
zyxucp
b05a4f51b7 add 增加清理聊天记录 2024-03-08 22:22:18 +08:00
zyxucp
608794b600 add 增加描述 2024-03-08 22:08:11 +08:00
zyxucp
ce8829ae69 add 新增知识库修改 2024-03-08 22:04:52 +08:00
zyxucp
1fb27f8d5a add 增加应用配置校验 2024-03-08 21:54:53 +08:00
zyxucp
caf8777290 add 增加模型管理配置 2024-03-08 21:47:51 +08:00
zyxucp
7dacdab2b5 fix 修改LLamaSharp本地调用 2024-03-08 21:12:42 +08:00
zyxucp
f6a8660144 fix 修改KMS删除 2024-03-08 20:25:25 +08:00
zyxucp
1e51631eba add 增加LLamaSharp本地执行 2024-03-08 19:53:13 +08:00
zyxucp
91048dc9c1 add 增加应用和知识库使用配置模型
fix 删除openai配置文件
2024-03-08 18:46:26 +08:00
zyxucp
9f8dc39e2f add 增加会话的模型和知识库模型配置 2024-03-08 17:06:35 +08:00
zyxucp
07aa8f4829 Update README.md 2024-03-08 16:55:13 +08:00
zyxucp
b504615b6d add 增加模型配置 2024-03-08 15:20:57 +08:00
zyxucp
3b1811a5ff add 增加模型配置 2024-03-08 14:50:46 +08:00
zyxucp
71be6d4a5a Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-08 10:04:01 +08:00
zyxucp
ae04d20a82 add 增加session 登陆记录 2024-03-08 00:48:41 +08:00
zyxucp
2ad3953d3f add session 2024-03-08 00:28:27 +08:00
zyxucp
c536f1d74b Update README.md 2024-03-07 21:46:06 +08:00
zyxucp
d8c1695ac9 Update README.en.md 2024-03-07 21:26:17 +08:00
zyxucp
b39912d08b Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-07 18:27:26 +08:00
zyxucp
8f5dd08836 Update README.md 2024-03-07 17:52:59 +08:00
zyxucp
6a27e61321 fix 删除appsetting注释 2024-03-07 17:32:59 +08:00
zyxucp
34fa19cf1e fix 修改docker file 2024-03-07 16:46:14 +08:00
38 changed files with 771 additions and 253 deletions

View File

@@ -7,7 +7,7 @@ COPY ["src/AntSK/AntSK.csproj", "AntSK/"]
RUN dotnet restore "AntSK/AntSK.csproj"
# Copy everything else and build
COPY . .
COPY src/ .
WORKDIR "/src/AntSK"
RUN dotnet build "AntSK.csproj" -c Release -o /app/build
RUN dotnet publish "AntSK.csproj" -c Release -o /app/publish

View File

@@ -123,58 +123,76 @@ The model supports openai by default. If you need to use azure openai and need t
The following configuration files need to be configured
## Using Docker Compose
Provided pg version appsettings. json and simplified version (Sqlite+disk) Docker Compose. simple. yml
Download Docker Compose.yml from the project root directory, and then place the configuration file appsettings.json and it in a unified directory,
The image of PG has been prepared here. You can modify the default account password in Docker Compose.yml, and your appsettings. json database connection needs to be consistent.
Then you can enter the directory and execute it
```
docker compose up - d
```
To start AntSK
Some meanings of configuration files
```
"ConnectionStrings":{
"Postgres": "Host=; Port=; Database=antsk; Username=; Password="
},
"OpenAIOption":{
"EndPoint": "",
"Key": "",
"Model": "",
"Embedding Model": """""
},
Postgres:{
"ConnectionString": "Host=; Port=; Database=antsk; Username=; Password=",
"TableNamePrefix": "km -"
},
"Login": {
"User": "admin",
"Password": "xuzeyu"
{
"DBConnection": {
"DbType": "Sqlite",
"ConnectionStrings": "Data Source=AntSK.db;"
},
"OpenAIOption": {
"EndPoint": "http://localhost:5000/llama/",
"Key": "NotNull",
"Model": "gpt4-turbo",
"EmbeddingModel": "text-embedding-ada-002"
},
"KernelMemory": {
"VectorDb": "Disk",
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
"TableNamePrefix": "km-"
},
"LLamaSharp": {
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
},
"Login": {
"User": "admin",
"Password": "xuzeyu"
},
"BackgroundTaskBroker": {
"ImportKMSTask": {
"WorkerCount": 1
}
}
}
```
I use CodeFirst mode. As long as the database link is configured, the table structure is automatically created
```
//Supports multiple databases, including SqlSugar, MySql, SqlServer, Sqlite, Oracle, PostgreSQL, Dm, Kdbndp, Oscar, MySqlConnector, Access, OpenGaussian, QuestDB, HG, ClickHouse, GBase, Odbc, OceanBaseForOracle, TDengine, GaussDB, OceanBase, Tidb, Vastbase, PolarDB, Custom
DBConnection DbType
//Connection string, corresponding strings need to be used according to different DB types
DBConnection ConnectionStrings
//You can use an online API that conforms to the OpenAI format (domestic models use one API adapter), or you can use AntSK's built-in llama API, with the IP and port being the AntSK startup address
OpenAIOption EndPoint
//Model key, if using a local model, it can default to Notnull. Chinese cannot be used here
OpenAIOption Key
//The type of vector storage supports Postgres Disk Memory, where Postgres requires the configuration of ConnectionString
KernelMemory VectorDb
//The running mode used by the local model is GUP CPU. If using an online API, you can freely use one
LLamaSharp RunType
//The model path of the local session model should pay attention to distinguishing between Linux and Windows drive letters
LLamaSharp Chat
//The model path of the local vector model should pay attention to distinguishing between Linux and Windows drive letters
LLamaSharp Embedding
//Default administrator account password
Login
//The number of threads for importing asynchronous processing can be higher when using online APIs. Local models suggest 1, otherwise memory overflow and crash may occur
BackgroundTaskBroker ImportKMSTask WorkerCount
If you want to use LLamaSharp to run the local model, you also need to set the following configuration:
```
"LLamaSharp": {
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf"
},
```
You need to configure the addresses of the Chat and Embedding models, and then modify EndPoint to local. When using the local model, parameters such as Key, Model, and Embedding Model are not used, so you can freely fill in these parameters:
```
"OpenAIOption": {
"EndPoint": "https://ip:port/llama/",
"Key": "",
"Model": "",
"EmbeddingModel": ""
},
```

109
README.md
View File

@@ -71,9 +71,11 @@ Login是默认的登陆账号和密码
## 使用docker-compose
从项目根目录下载docker-compose.yml,然后把配置文件appsettings.json和它放在统一目录
提供了pg版本 **appsettings.json** 和 简化版本Sqlite+disk **docker-compose.simple.yml**
这里已经把pg的镜像做好了。在docker-compose.yml中可以修改默认账号密码,然后你的appsettings.json的数据库连接需要保持一致。
从项目根目录下载**docker-compose.yml**,然后把配置文件**appsettings.json**和它放在统一目录,
这里已经把pg的镜像做好了。在docker-compose.yml中可以修改默认账号密码然后你的**appsettings.json**的数据库连接需要保持一致。
然后你可以进入到目录后执行
```
@@ -81,46 +83,106 @@ docker-compose up -d
```
来启动AntSK
## 如何在docker中挂载本地模型
```
# 非 host 版本, 不使用本机代理
version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.5
ports:
- 5000:5000
networks:
- antsk
depends_on:
- antskpg
restart: always
environment:
- ASPNETCORE_URLS=http://*:5000
volumes:
- ./appsettings.json:/app/appsettings.json # 本地配置文件 需要放在同级目录
- D://model:/app/model
networks:
antsk:
```
以这个为示例意思是把windows本地D://model的文件夹挂载进 容器内/app/model 如果是这样你的appsettings.json中的模型地址应该配置为
```
model/xxx.gguf
```
## 配置文件的一些含义
```
"ConnectionStrings": {
"Postgres": "Host=;Port=;Database=antsk;Username=;Password="//这个是业务数据的连接字符串
{
"DBConnection": {
"DbType": "Sqlite",
"ConnectionStrings": "Data Source=AntSK.db;"
},
"OpenAIOption": {
"EndPoint": "", //openai协议的接口写到v1之前
"Key": "",//接口秘钥,如果使用本地模型可以随意填写一个但不能为空
"Model": "",//会话模型,使用接口时需要,使用本地模型可以随意填写
"EmbeddingModel": ""//向量模型,使用接口时需要,使用本地模型可以随意填写
"EndPoint": "http://localhost:5000/llama/",
"Key": "NotNull",
"Model": "gpt4-turbo",
"EmbeddingModel": "text-embedding-ada-002"
},
"Postgres": {
"KernelMemory": {
"VectorDb": "Disk",
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
"TableNamePrefix": "km-"
},
"LLamaSharp": {
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
},
"Login": {
"User": "admin",
"Password": "xuzeyu"
},
"BackgroundTaskBroker": {
"ImportKMSTask": {
"WorkerCount": 1
}
}
}
```
我使用的是CodeFirst模式只要配置好数据库链接表结构是自动创建的
如果想使用LLamaSharp运行本地模型还需要设置如下配置
```
"LLamaSharp": {
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf",//本地会话模型的磁盘路径
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf"//本地向量模型的磁盘路径
},
//支持多种数据库具体可以查看SqlSugarMySqlSqlServerSqliteOraclePostgreSQLDmKdbndpOscarMySqlConnectorAccessOpenGaussQuestDBHGClickHouseGBaseOdbcOceanBaseForOracleTDengineGaussDBOceanBaseTidbVastbasePolarDBCustom
DBConnection.DbType
//连接字符串需要根据不同DB类型用对应的字符串
DBConnection.ConnectionStrings
//可以使用符合openai格式的在线API国产模型使用one-api转接 也可以使用AntSK自带的llama apiip和端口是AntSK启动地址
OpenAIOption.EndPoint
//模型秘钥如果使用本地模型可以默认NotNull 这里不能用中文
OpenAIOption.Key
//向量存储的类型,支持 Postgres Disk Memory 其中Postgres需要配置 ConnectionString
KernelMemory.VectorDb
//本地模型使用的运行方式 GUP CPU ,如果用在线API 这个随意使用一个即可
LLamaSharp.RunType
//本地会话模型的模型路径 注意区分linux和windows盘符不同
LLamaSharp.Chat
//本地向量模型的模型路径 注意区分linux和windows盘符不同
LLamaSharp.Embedding
//默认管理员账号密码
Login
//导入异步处理的线程数使用在线API可以高一点本地模型建议1 否则容易内存溢出崩掉
BackgroundTaskBroker.ImportKMSTask.WorkerCount
```
需要配置Chat和Embedding模型的地址然后修改EndPoint为本地使用本地模型时并没有用到Key、Model、EmbeddingModel这些参数所以这几个你可以随意填写
## 找不到样式问题解决
AntSK/src/AntSK下执行:
```
"OpenAIOption": {
"EndPoint": "http://ip:port/llama/",//如果使用本地模型这个ip端口是AntSK服务启动的ip和端口
"Key": "",//接口秘钥,如果使用本地模型可以随意填写一个但不能为空
"Model": "",//会话模型,使用接口时需要,使用本地模型可以随意填写
"EmbeddingModel": ""//向量模型,使用接口时需要,使用本地模型可以随意填写
},
dotnet clean
dotnet build
dotnet publish "AntSK.csproj"
```
再去AntSK/src/AntSK/bin/Release/net8.0/publish下
```
dotnet AntSK.dll
```
然后启动就有样式了
DB我使用的是CodeFirst模式只要配置好数据库链接表结构是自动创建的
想了解更多信息或开始使用 **AntSK**,可以关注我的公众号以及加入交流群。
@@ -132,3 +194,4 @@ docker-compose up -d
---
我们对您在**AntSK**的兴趣表示感谢,并期待与您携手共创智能化的未来!

View File

@@ -48,7 +48,7 @@
<param name="value"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.GetKernel(System.String,System.String)">
<member name="M:AntSK.Domain.Domain.Service.KernelService.GetKernelByApp(AntSK.Domain.Repositories.Apps)">
<summary>
获取kernel实例依赖注入不好按每个用户去Import不同的插件所以每次new一个新的kernel
</summary>
@@ -78,6 +78,11 @@
<param name="history"></param>
<returns></returns>
</member>
<member name="F:AntSK.Domain.Domain.Service.LLamaConfig.dicLLamaWeights">
<summary>
避免模型重复加载,本地缓存
</summary>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTOList``1(System.Object)">
<summary>
Entity集合转DTO集合
@@ -103,6 +108,16 @@
<param name="result"></param>
<returns></returns>
</member>
<member name="T:AntSK.Domain.Model.Enum.AIType">
<summary>
AI类型
</summary>
</member>
<member name="T:AntSK.Domain.Model.Enum.AIModelType">
<summary>
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Model.MessageInfo.IsSend">
<summary>
发送是true 接收是false
@@ -208,6 +223,11 @@
类型
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.ChatModelID">
<summary>
会话模型ID
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Temperature">
<summary>
温度
@@ -278,6 +298,16 @@
会话模型
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Kmss.ChatModelID">
<summary>
会话模型ID
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Kmss.EmbeddingModelID">
<summary>
向量模型ID
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Kmss.MaxTokensPerParagraph">
<summary>
每个段落的最大标记数。
@@ -550,6 +580,11 @@
sqlserver连接
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.AIType">
<summary>
AI类型
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.AIModelType">
<summary>
模型类型
@@ -570,6 +605,11 @@
模型秘钥
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.AIModels.ModelDescription">
<summary>
部署名azure需要使用
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Users.No">
<summary>
工号,用于登陆

View File

@@ -11,7 +11,7 @@ namespace AntSK.Domain.Domain.Interface
{
public interface IKMService
{
MemoryServerless GetMemory(SearchClientConfig searchClientConfig = null, TextPartitioningOptions textPartitioningOptions = null);
Task<List<KMFile>> GetDocumentByFileID(string fileid);
MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null);
Task<List<KMFile>> GetDocumentByFileID(string kmsid, string fileid);
}
}

View File

@@ -10,7 +10,7 @@ namespace AntSK.Domain.Domain.Interface
{
public interface IKernelService
{
Kernel GetKernel(string modelId = null, string apiKey = null);
Kernel GetKernelByApp(Apps app);
void ImportFunctionsByApp(Apps app, Kernel _kernel);
Task<string> HistorySummarize(Kernel _kernel, string questions, string history);
}

View File

@@ -26,12 +26,8 @@ namespace AntSK.Domain.Domain.Service
try
{
var km = _kmss_Repositories.GetFirst(p => p.Id == req.KmsId);
var _memory = _kMService.GetMemory(textPartitioningOptions: new TextPartitioningOptions()
{
MaxTokensPerLine = km.MaxTokensPerLine,
MaxTokensPerParagraph = km.MaxTokensPerParagraph,
OverlappingTokens = km.OverlappingTokens
});
var _memory = _kMService.GetMemoryByKMS(km.Id);
string fileid = req.KmsDetail.Id;
switch (req.ImportType)
{
@@ -43,7 +39,7 @@ namespace AntSK.Domain.Domain.Service
.AddTag("kmsid", req.KmsId)
, index: "kms").Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(fileid).Result;
var docTextList = _kMService.GetDocumentByFileID(km.Id,fileid).Result;
string fileGuidName = Path.GetFileName(req.FilePath);
req.KmsDetail.FileName = req.FileName;
req.KmsDetail.FileGuidName = fileGuidName;
@@ -57,7 +53,7 @@ namespace AntSK.Domain.Domain.Service
var importResult = _memory.ImportWebPageAsync(req.Url, fileid, new TagCollection() { { "kmsid", req.KmsId } }
, index: "kms").Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(fileid).Result;
var docTextList = _kMService.GetDocumentByFileID(km.Id,fileid).Result;
req.KmsDetail.Url = req.Url;
req.KmsDetail.DataCount = docTextList.Count;
}
@@ -68,7 +64,7 @@ namespace AntSK.Domain.Domain.Service
var importResult = _memory.ImportTextAsync(req.Text, fileid, new TagCollection() { { "kmsid", req.KmsId } }
, index: "kms").Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(fileid).Result;
var docTextList = _kMService.GetDocumentByFileID(km.Id,fileid).Result;
req.KmsDetail.Url = req.Url;
req.KmsDetail.DataCount = docTextList.Count;

View File

@@ -11,18 +11,33 @@ using System.Net.Http;
using Microsoft.Extensions.Options;
using Microsoft.KernelMemory.Configuration;
using Microsoft.Extensions.Configuration;
using AntSK.Domain.Repositories;
using LLamaSharp.KernelMemory;
using LLama.Common;
using DocumentFormat.OpenXml.Spreadsheet;
using LLama;
namespace AntSK.Domain.Domain.Service
{
[ServiceDescription(typeof(IKMService), ServiceLifetime.Scoped)]
public class KMService(
IConfiguration _config
IConfiguration _config,
IKmss_Repositories _kmss_Repositories,
IAIModels_Repositories _aIModels_Repositories
) : IKMService
{
public MemoryServerless GetMemory(SearchClientConfig searchClientConfig = null, TextPartitioningOptions textPartitioningOptions = null)
public MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null)
{
var handler = new OpenAIHttpClientHandler();
var httpClient = new HttpClient(handler);
//获取KMS配置
var kms = _kmss_Repositories.GetFirst(p => p.Id == kmsID);
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.ChatModelID);
var embedModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.EmbeddingModelID);
//http代理
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
var embeddingHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(embedModel.EndPoint);
//搜索配置
if (searchClientConfig.IsNull())
{
searchClientConfig = new SearchClientConfig
@@ -34,33 +49,83 @@ namespace AntSK.Domain.Domain.Service
};
}
if (textPartitioningOptions.IsNull())
{
textPartitioningOptions = new TextPartitioningOptions
{
MaxTokensPerLine = 99,
MaxTokensPerParagraph = 299,
OverlappingTokens = 47
};
}
var memory = new KernelMemoryBuilder()
.WithSimpleFileStorage(new SimpleFileStorageConfig { StorageType = FileSystemTypes.Volatile, Directory = "_files" })
.WithSearchClientConfig(searchClientConfig)
.WithCustomTextPartitioningOptions(textPartitioningOptions)
.WithOpenAITextGeneration(new OpenAIConfig()
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
{
APIKey = OpenAIOption.Key,
TextModel = OpenAIOption.Model
MaxTokensPerLine = kms.MaxTokensPerLine,
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
OverlappingTokens = kms.OverlappingTokens
});
//加载huihu 模型
WithTextGenerationByAIType(memory, chatModel, chatHttpClient);
//加载向量模型
WithTextEmbeddingGenerationByAIType(memory,embedModel, embeddingHttpClient);
//加载向量库
WithMemoryDbByVectorDB(memory, _config);
}, null, httpClient)
.WithOpenAITextEmbeddingGeneration(new OpenAIConfig()
var result = memory.Build<MemoryServerless>();
return result;
}
private void WithTextEmbeddingGenerationByAIType(IKernelMemoryBuilder memory,AIModels embedModel, HttpClient embeddingHttpClient )
{
switch (embedModel.AIType)
{
APIKey = OpenAIOption.Key,
EmbeddingModel = OpenAIOption.EmbeddingModel
case Model.Enum.AIType.OpenAI:
memory.WithOpenAITextEmbeddingGeneration(new OpenAIConfig()
{
APIKey = embedModel.ModelKey,
EmbeddingModel = embedModel.ModelName
}, null, false, embeddingHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
memory.WithAzureOpenAITextEmbeddingGeneration(new AzureOpenAIConfig()
{
APIKey = embedModel.ModelKey,
Deployment = embedModel.ModelName.ConvertToString(),
Endpoint = embedModel.EndPoint.ConvertToString()
});
break;
case Model.Enum.AIType.LLamaSharp:
var (weights, parameters) = LLamaConfig.GetLLamaConfig(embedModel.ModelName);
var embedder = new LLamaEmbedder(weights, parameters);
memory.WithLLamaSharpTextEmbeddingGeneration(new LLamaSharpTextEmbeddingGenerator(embedder));
break;
}
}
}, null, false, httpClient);
private void WithTextGenerationByAIType(IKernelMemoryBuilder memory,AIModels chatModel, HttpClient chatHttpClient )
{
switch (chatModel.AIType)
{
case Model.Enum.AIType.OpenAI:
memory.WithOpenAITextGeneration(new OpenAIConfig()
{
APIKey = chatModel.ModelKey,
TextModel = chatModel.ModelName
}, null, chatHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
memory.WithAzureOpenAITextGeneration(new AzureOpenAIConfig()
{
APIKey = chatModel.ModelKey,
Deployment = chatModel.ModelName.ConvertToString(),
Endpoint = chatModel.EndPoint.ConvertToString()
});
break;
case Model.Enum.AIType.LLamaSharp:
var (weights, parameters) = LLamaConfig.GetLLamaConfig(chatModel.ModelName);
var context = weights.CreateContext(parameters);
var executor = new StatelessExecutor(weights, parameters);
memory.WithLLamaSharpTextGeneration(new LlamaSharpTextGenerator(weights, context, executor));
break;
}
}
private void WithMemoryDbByVectorDB(IKernelMemoryBuilder memory,IConfiguration _config)
{
string VectorDb = _config["KernelMemory:VectorDb"].ConvertToString();
string ConnectionString = _config["KernelMemory:ConnectionString"].ConvertToString();
string TableNamePrefix = _config["KernelMemory:TableNamePrefix"].ConvertToString();
@@ -86,14 +151,11 @@ namespace AntSK.Domain.Domain.Service
});
break;
}
var result = memory.Build<MemoryServerless>();
return result;
}
public async Task<List<KMFile>> GetDocumentByFileID(string fileid)
public async Task<List<KMFile>> GetDocumentByFileID(string kmsid,string fileid)
{
var _memory = GetMemory();
var _memory = GetMemoryByKMS(kmsid);
var memories = await _memory.ListIndexesAsync();
var memoryDbs = _memory.Orchestrator.GetMemoryDbs();
List<KMFile> docTextList = new List<KMFile>();
@@ -120,5 +182,7 @@ namespace AntSK.Domain.Domain.Service
}
return docTextList;
}
}
}

View File

@@ -4,8 +4,15 @@ using AntSK.Domain.Model;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using DocumentFormat.OpenXml.EMMA;
using LLama;
using LLamaSharp.KernelMemory;
using LLamaSharp.SemanticKernel.TextCompletion;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.KernelMemory;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.Core;
using Microsoft.SemanticKernel.TextGeneration;
using RestSharp;
using System;
using System.Collections.Generic;
@@ -13,35 +20,70 @@ using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
namespace AntSK.Domain.Domain.Service
{
[ServiceDescription(typeof(IKernelService), ServiceLifetime.Scoped)]
public class KernelService(
IApis_Repositories _apis_Repositories
) : IKernelService
public class KernelService: IKernelService
{
private readonly IApis_Repositories _apis_Repositories;
private readonly IAIModels_Repositories _aIModels_Repositories;
public KernelService(
IApis_Repositories apis_Repositories,
IAIModels_Repositories aIModels_Repositories
)
{
_apis_Repositories = apis_Repositories;
_aIModels_Repositories = aIModels_Repositories;
}
/// <summary>
/// 获取kernel实例依赖注入不好按每个用户去Import不同的插件所以每次new一个新的kernel
/// </summary>
/// <param name="modelId"></param>
/// <param name="apiKey"></param>
/// <returns></returns>
public Kernel GetKernel(string modelId=null,string apiKey=null)
public Kernel GetKernelByApp(Apps app)
{
var handler = new OpenAIHttpClientHandler();
var httpClient = new HttpClient(handler);
httpClient.Timeout = TimeSpan.FromMinutes(5);
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: modelId!=null? modelId : OpenAIOption.Model,
apiKey: apiKey!=null? apiKey: OpenAIOption.Key,
httpClient: httpClient)
.Build();
var chatModel= _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);
var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);
var builder = Kernel.CreateBuilder();
WithTextGenerationByAIType(builder, chatModel, chatHttpClient);
var kernel= builder.Build();
RegisterPluginsWithKernel(kernel);
return kernel;
}
private void WithTextGenerationByAIType(IKernelBuilder builder, AIModels chatModel, HttpClient chatHttpClient)
{
switch (chatModel.AIType)
{
case Model.Enum.AIType.OpenAI:
builder.AddOpenAIChatCompletion(
modelId: chatModel.ModelName,
apiKey: chatModel.ModelKey,
httpClient: chatHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
builder.AddAzureOpenAIChatCompletion(
deploymentName:chatModel.ModelName,
apiKey: chatModel.ModelKey,
endpoint: chatModel.EndPoint
);
break;
case Model.Enum.AIType.LLamaSharp:
var (weights, parameters) = LLamaConfig.GetLLamaConfig(chatModel.ModelName);
var ex = new StatelessExecutor(weights, parameters);
builder.Services.AddKeyedSingleton<ITextGenerationService>("local-llama", new LLamaSharpTextCompletion(ex));
break;
}
}
/// <summary>
/// 根据app配置的插件导入插件
/// </summary>

View File

@@ -0,0 +1,49 @@
using LLama.Common;
using LLama;
using LLamaSharp.KernelMemory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Service
{
public static class LLamaConfig
{
static object lockobj = new object();
/// <summary>
/// 避免模型重复加载,本地缓存
/// </summary>
static Dictionary<string, (LLamaWeights, ModelParams)> dicLLamaWeights = new Dictionary<string, (LLamaWeights, ModelParams)>();
public static (LLamaWeights, ModelParams) GetLLamaConfig(string modelPath, LLamaSharpConfig config =null)
{
lock (lockobj)
{
if (dicLLamaWeights.ContainsKey(modelPath))
{
return dicLLamaWeights.GetValueOrDefault(modelPath);
}
else
{
InferenceParams infParams = new() { AntiPrompts = ["\n\n"] };
LLamaSharpConfig lsConfig = new(modelPath) { DefaultInferenceParams = infParams };
if (config!=null)
{
lsConfig = config;
}
var parameters = new ModelParams(lsConfig.ModelPath)
{
ContextSize = lsConfig?.ContextSize ?? 2048,
Seed = lsConfig?.Seed ?? 0,
GpuLayerCount = lsConfig?.GpuLayerCount ?? 20,
EmbeddingMode = true
};
var weights = LLamaWeights.LoadFromFile(parameters);
dicLLamaWeights.Add(modelPath, (weights, parameters));
return (weights, parameters);
}
}
}
}
}

View File

@@ -6,6 +6,19 @@ using System.Threading.Tasks;
namespace AntSK.Domain.Model.Enum
{
/// <summary>
/// AI类型
/// </summary>
public enum AIType
{
OpenAI = 1,
AzureOpenAI = 2,
LLamaSharp=3
}
/// <summary>
/// 模型类型
/// </summary>
public enum AIModelType
{
Chat = 1,

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Options
{
public class OpenAIOption
{
public static string EndPoint { get; set; }
public static string Key { get; set; }
public static string Model { get; set; }
public static string EmbeddingModel { get; set; }
}
}

View File

@@ -37,6 +37,12 @@ namespace AntSK.Domain.Repositories
[Required]
public string Type { get; set; }
/// <summary>
/// 会话模型ID
/// </summary>
[Required]
public string? ChatModelID { get; set; }
/// <summary>
/// 温度
/// </summary>

View File

@@ -28,6 +28,17 @@ namespace AntSK.Domain.Repositories
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 会话模型ID
/// </summary>
[Required]
public string? ChatModelID { get; set; }
/// <summary>
/// 向量模型ID
/// </summary>
[Required]
public string? EmbeddingModelID { get; set; }
/// <summary>
/// 每个段落的最大标记数。
@@ -48,5 +59,7 @@ namespace AntSK.Domain.Repositories
/// </summary>
[SugarColumn(DefaultValue = "49")]
public int OverlappingTokens { get; set; } = 49;
}
}

View File

@@ -15,25 +15,36 @@ namespace AntSK.Domain.Repositories
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// AI类型
/// </summary>
[Required]
[SugarColumn(DefaultValue = "1")]
public AIType AIType { get; set; } = AIType.OpenAI;
/// <summary>
/// 模型类型
/// </summary>
[Required]
public AIModelType AIModelType { get; set; }
public AIModelType AIModelType { get; set; }= AIModelType.Chat;
/// <summary>
/// 模型地址
/// </summary>
[Required]
public string EndPoint { get; set; }
public string EndPoint { get; set; } = "";
/// <summary>
/// 模型名称
/// </summary>
[Required]
public string ModelName { get; set; }
public string ModelName { get; set; } = "";
/// <summary>
/// 模型秘钥
/// </summary>
[Required]
public string ModelKey { get; set; }
public string ModelKey { get; set; } = "";
/// <summary>
/// 部署名azure需要使用
/// </summary>
[Required]
public string ModelDescription { get; set; }
}

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -10,11 +11,17 @@ namespace AntSK.Domain.Utils
{
public class OpenAIHttpClientHandler : HttpClientHandler
{
private string _endPoint { get; set; }
public OpenAIHttpClientHandler(string endPoint)
{
this._endPoint = endPoint;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
UriBuilder uriBuilder;
Regex regex = new Regex(@"(https?)://([^/:]+)(:\d+)?/(.*)");
Match match = regex.Match(OpenAIOption.EndPoint);
Match match = regex.Match(_endPoint);
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" && request.Content != null)
{
string requestBody = await request.Content.ReadAsStringAsync();
@@ -74,4 +81,16 @@ namespace AntSK.Domain.Utils
return response;
}
}
public class OpenAIHttpClientHandlerUtil
{
public static HttpClient GetHttpClient( string endPoint)
{
var handler = new OpenAIHttpClientHandler(endPoint.ConvertToString());
var httpClient = new HttpClient(handler);
httpClient.Timeout = TimeSpan.FromMinutes(5);
return httpClient;
}
}
}

View File

@@ -69,7 +69,7 @@
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.Chat.HistorySummarize(System.String)">
<member name="M:AntSK.Pages.ChatPage.Chat.HistorySummarize(AntSK.Domain.Repositories.Apps,System.String)">
<summary>
历史会话的会话总结
</summary>
@@ -94,7 +94,7 @@
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Pages.ChatPage.OpenChat.HistorySummarize(System.String)">
<member name="M:AntSK.Pages.ChatPage.OpenChat.HistorySummarize(AntSK.Domain.Repositories.Apps,System.String)">
<summary>
历史会话的会话总结
</summary>
@@ -136,7 +136,7 @@
<param name="app"></param>
<returns></returns>
</member>
<member name="M:AntSK.Services.OpenApi.OpenApiService.HistorySummarize(AntSK.Models.OpenAIModel)">
<member name="M:AntSK.Services.OpenApi.OpenApiService.HistorySummarize(AntSK.Domain.Repositories.Apps,AntSK.Models.OpenAIModel)">
<summary>
历史会话的会话总结
</summary>

View File

@@ -4,6 +4,7 @@
@using Microsoft.AspNetCore.Components.Authorization
@using AntSK.Domain.Options
@using AntSK.Domain.Repositories
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inherits LayoutComponentBase
<AntDesign.ProLayout.BasicLayout
@@ -28,6 +29,7 @@
[Inject] public HttpClient HttpClient { get; set; }
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
[Inject] protected ProtectedSessionStorage _protectedSessionStore { get; set; }
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
protected override async Task OnInitializedAsync()
@@ -35,15 +37,30 @@
await base.OnInitializedAsync();
//菜单权限控制
var menuList = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
if ((bool)context?.Identity.IsAuthenticated)
var userSessionStorageResult = await _protectedSessionStore.GetAsync<UserSession>("UserSession");
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
if (userSession != null || (bool)context?.Identity.IsAuthenticated)
{
if (context.Identity.Name == LoginOption.User)
string username = "";
if (userSession != null)
{
username = userSession.UserName;
}
else
{
username = context.Identity.Name;
}
if (username == LoginOption.User )
{
_menuData = menuList;
}
else
{
var userMenuList = _users_Repositories.GetFirst(p => p.No == context.Identity.Name).MenuRole.Split(",").ToList();
var userMenuList = _users_Repositories.GetFirst(p => p.No == username).MenuRole.Split(",").ToList();
//非管理员用户不允许操作系统设置
_menuData = menuList.Where(p => p.Key != "setting" && userMenuList.Contains(p.Key)).ToArray();
}

View File

@@ -0,0 +1,8 @@
namespace AntSK.Models
{
public class UserSession
{
public string UserName { get; set; }
public string Role { get; set; }
}
}

View File

@@ -28,6 +28,14 @@
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
</FormItem>
<FormItem Label="会话模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@_chatList"
@bind-Value="@context.ChatModelID"
ValueProperty="c=>c.Id"
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
</Select>
</FormItem>
@if (@context.Type == "chat")
{

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Components;
using AntSK.Domain.Repositories;
using AntSK.Models;
using System.IO;
using AntSK.Domain.Model.Enum;
namespace AntSK.Pages.AppPage
{
@@ -23,6 +24,8 @@ namespace AntSK.Pages.AppPage
protected NavigationManager NavigationManager { get; set; }
[Inject]
protected MessageService? Message { get; set; }
[Inject]
protected IAIModels_Repositories _aimodels_Repositories { get; set; }
private Apps _appModel = new Apps() ;
@@ -34,12 +37,16 @@ namespace AntSK.Pages.AppPage
private List<Apis> _apiList = new List<Apis>();
private List<AIModels> _chatList { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_kmsList = _kmss_Repositories.GetList();
_apiList= _apis_Repositories.GetList();
_chatList= _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Chat);
if (!string.IsNullOrEmpty(AppId))
{
//查看
@@ -52,6 +59,22 @@ namespace AntSK.Pages.AppPage
}
private void HandleSubmit()
{
if (kmsIds != null && kmsIds.Count() > 0)
{
var kmsList = _kmss_Repositories.GetList(p => kmsIds.Contains(p.Id));
bool allSameEmbeddingModelID = kmsList.Select(k => k.EmbeddingModelID).Distinct().Count() == 1;
if (!allSameEmbeddingModelID)
{
_ = Message.Error("同一个应用的知识库的Embedding模型必须相同", 2);
return;
}
_appModel.KmsIdList = string.Join(",", kmsIds);
}
if (apiIds != null && apiIds.Count() > 0)
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
if (string.IsNullOrEmpty(AppId))
{
//新增
@@ -64,30 +87,14 @@ namespace AntSK.Pages.AppPage
return;
}
if (kmsIds != null && kmsIds.Count() > 0)
{
_appModel.KmsIdList = string.Join(",", kmsIds);
}
if (apiIds != null && apiIds.Count() > 0)
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
_apps_Repositories.Insert(_appModel);
}
else {
//修改
if (kmsIds != null && kmsIds.Count() > 0)
{
_appModel.KmsIdList = string.Join(",", kmsIds);
}
if (apiIds != null && apiIds.Count() > 0)
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
//修改
_apps_Repositories.Update(_appModel);
}
//NavigationManager.NavigateTo($"/app/detail/{_appModel.Id}");
NavigationManager.NavigateTo($"/applist");
}

View File

@@ -59,6 +59,7 @@
<div style="height: 10px;"></div>
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
<Suffix>
<Button Icon="clear" Type="@(ButtonType.Link)" OnClick="@(async () => await OnClearAsync())" Disabled="@Sendding"></Button>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
</Suffix>
</AntDesign.Input>

View File

@@ -6,6 +6,7 @@ using AntSK.Domain.Utils;
using Azure.AI.OpenAI;
using Azure.Core;
using DocumentFormat.OpenXml.EMMA;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Wordprocessing;
using MarkdownSharp;
using Microsoft.AspNetCore.Components;
@@ -43,6 +44,8 @@ namespace AntSK.Pages.ChatPage
protected IKernelService _kernelService { get; set; }
[Inject]
protected IKMService _kMService { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
protected bool _loading = false;
protected List<MessageInfo> MessageList = [];
@@ -102,24 +105,33 @@ namespace AntSK.Pages.ChatPage
});
}
protected async Task OnClearAsync(string id)
{
await Task.Run(() =>
protected async Task OnClearAsync() {
if (MessageList.Count > 0)
{
MessageList = MessageList.Where(w => w.ID != id).ToList();
});
var content = "是否要清理会话记录";
var title = "清理";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
MessageList.Clear();
_ = Message.Info("清理成功");
}
}
else
{
_ = Message.Info("没有会话记录");
}
}
protected async Task<bool> SendAsync(string questions)
{
string msg = "";
//处理多轮会话
Apps app = _apps_Repositories.GetFirst(p => p.Id == AppId);
if (MessageList.Count > 0)
{
msg = await HistorySummarize(questions);
msg = await HistorySummarize(app,questions);
}
Apps app = _apps_Repositories.GetFirst(p => p.Id == AppId);
switch (app.Type)
{
case "chat":
@@ -144,12 +156,12 @@ namespace AntSK.Pages.ChatPage
/// <returns></returns>
private async Task SendKms(string questions, string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
var _memory = _kMService.GetMemory();
var _kernel = _kernelService.GetKernelByApp(app);
//知识库问答
var filters = new List<MemoryFilter>();
var kmsidList = app.KmsIdList.Split(",");
//只取第一个知识库的配置
var _memory = _kMService.GetMemoryByKMS(kmsidList.FirstOrDefault());
foreach (var kmsid in kmsidList)
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
@@ -176,7 +188,7 @@ namespace AntSK.Pages.ChatPage
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: jsonFun,
var chatResult = _kernel.InvokeStreamingAsync<StreamingTextContent>(function: jsonFun,
arguments: new KernelArguments() { ["doc"] = dataMsg, ["history"] = msg, ["questions"]=questions });
MessageInfo info = null;
@@ -187,15 +199,15 @@ namespace AntSK.Pages.ChatPage
{
info = new MessageInfo();
info.ID = Guid.NewGuid().ToString();
info.Context = content?.Content?.ConvertToString();
info.HtmlAnswers = content?.Content?.ConvertToString();
info.Context = content?.Text?.ConvertToString();
info.HtmlAnswers = content?.Text?.ConvertToString();
info.CreateTime = DateTime.Now;
MessageList.Add(info);
}
else
{
info.HtmlAnswers += content.Content;
info.HtmlAnswers += content.Text;
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
@@ -219,7 +231,7 @@ namespace AntSK.Pages.ChatPage
/// <returns></returns>
private async Task SendChat(string questions, string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
var _kernel = _kernelService.GetKernelByApp(app);
if (string.IsNullOrEmpty(app.Prompt)||!app.Prompt.Contains("{{$input}}"))
{
//如果模板为空,给默认提示词
@@ -272,9 +284,9 @@ namespace AntSK.Pages.ChatPage
/// </summary>
/// <param name="questions"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(string questions)
private async Task<string> HistorySummarize(Apps app,string questions)
{
var _kernel = _kernelService.GetKernel();
var _kernel = _kernelService.GetKernelByApp(app);
if (MessageList.Count > 1)
{
StringBuilder history = new StringBuilder();

View File

@@ -43,6 +43,7 @@
<div style="flex-shrink:0;margin:10px;">
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
<Suffix>
<Button Icon="clear" Type="@(ButtonType.Link)" OnClick="@(async () => await OnClearAsync())" Disabled="@Sendding"></Button>
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
</Suffix>
</AntDesign.Input>

View File

@@ -38,6 +38,8 @@ namespace AntSK.Pages.ChatPage
protected IKernelService _kernelService { get; set; }
[Inject]
protected IKMService _kMService { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
protected bool _loading = false;
protected List<MessageInfo> MessageList = [];
@@ -52,6 +54,25 @@ namespace AntSK.Pages.ChatPage
await base.OnInitializedAsync();
app = _apps_Repositories.GetFirst(p=>p.Id==AppId);
}
protected async Task OnClearAsync()
{
if (MessageList.Count > 0)
{
var content = "是否要清理会话记录";
var title = "清理";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
MessageList.Clear();
_ = Message.Info("清理成功");
}
}
else
{
_ = Message.Info("没有会话记录");
}
}
protected async Task OnSendAsync()
{
try
@@ -103,12 +124,11 @@ namespace AntSK.Pages.ChatPage
{
string msg = "";
//处理多轮会话
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
if (MessageList.Count > 0)
{
msg = await HistorySummarize(questions);
msg = await HistorySummarize(app,questions);
}
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
switch (app.Type)
{
case "chat":
@@ -133,8 +153,8 @@ namespace AntSK.Pages.ChatPage
/// <returns></returns>
private async Task SendKms(string questions, string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
var _memory = _kMService.GetMemory();
var _kernel = _kernelService.GetKernelByApp(app);
var _memory = _kMService.GetMemoryByKMS(app.KmsIdList.Split(",").FirstOrDefault());
//知识库问答
var filters = new List<MemoryFilter>();
@@ -200,7 +220,7 @@ namespace AntSK.Pages.ChatPage
/// <returns></returns>
private async Task SendChat(string questions, string msg, Apps app)
{
var _kernel= _kernelService.GetKernel();
var _kernel= _kernelService.GetKernelByApp(app);
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
{
//如果模板为空,给默认提示词
@@ -248,9 +268,9 @@ namespace AntSK.Pages.ChatPage
/// </summary>
/// <param name="questions"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(string questions)
private async Task<string> HistorySummarize(Apps app,string questions)
{
var _kernel = _kernelService.GetKernel();
var _kernel = _kernelService.GetKernelByApp(app);
if (MessageList.Count > 1)
{
StringBuilder history = new StringBuilder();

View File

@@ -2,6 +2,7 @@
@using AntSK.Domain.Repositories
@using AntSK.Models
@page "/Kms/Add"
@page "/Kms/Add/{KmsId}"
@inject IMessageService _message
@using AntSK.Services.Auth
@inherits AuthComponentBase
@@ -23,6 +24,20 @@
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
</FormItem>
<FormItem Label="会话模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@_chatList"
@bind-Value="@context.ChatModelID"
ValueProperty="c=>c.Id"
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+ c.ModelDescription">
</Select>
</FormItem>
<FormItem Label="嵌入模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@_embeddingList"
@bind-Value="@context.EmbeddingModelID"
ValueProperty="c=>c.Id"
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
</Select>
</FormItem>
<FormItem Label="段落切片数(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<AntDesign.InputNumber @bind-Value="context.MaxTokensPerParagraph" PlaceHolder="段落切片数"></AntDesign.InputNumber>
</FormItem>

View File

@@ -3,12 +3,16 @@ using Microsoft.AspNetCore.Components;
using AntSK.Domain.Repositories;
using AntSK.Models;
using System.IO;
using AntSK.Domain.Model.Enum;
using DocumentFormat.OpenXml.Wordprocessing;
namespace AntSK.Pages.KmsPage
{
public partial class AddKms
{
[Parameter]
public string KmsId { get; set; }
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
@@ -16,16 +20,27 @@ namespace AntSK.Pages.KmsPage
[Inject]
protected MessageService? Message { get; set; }
private readonly Kmss _kmsModel = new Kmss() ;
[Inject]
protected IAIModels_Repositories _aimodels_Repositories { get; set; }
private Kmss _kmsModel = new Kmss();
private List<AIModels> _chatList { get; set; }
private List<AIModels> _embeddingList { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_chatList = _aimodels_Repositories.GetList(p=>p.AIModelType==AIModelType.Chat);
_embeddingList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Embedding);
if (!string.IsNullOrEmpty(KmsId))
{
//查看
_kmsModel =await _kmss_Repositories.GetFirstAsync(p => p.Id == KmsId);
}
}
private void HandleSubmit()
{
_kmsModel.Id = Guid.NewGuid().ToString();
if (_kmss_Repositories.IsAny(p => p.Name == _kmsModel.Name))
{
_ = Message.Error("名称已存在!", 2);
return;
}
if (_kmsModel.OverlappingTokens >= _kmsModel.MaxTokensPerLine || _kmsModel.OverlappingTokens >= _kmsModel.MaxTokensPerParagraph)
{
_ = Message.Error("重叠部分需小于行切片和段落切片!", 2);
@@ -37,7 +52,21 @@ namespace AntSK.Pages.KmsPage
_ = Message.Error("行切片需小于段落切片!", 2);
return;
}
_kmss_Repositories.Insert(_kmsModel);
if (string.IsNullOrEmpty(KmsId))
{
_kmsModel.Id = Guid.NewGuid().ToString();
if (_kmss_Repositories.IsAny(p => p.Name == _kmsModel.Name))
{
_ = Message.Error("名称已存在!", 2);
return;
}
_kmss_Repositories.Insert(_kmsModel);
}
else
{
_kmss_Repositories.Update(_kmsModel);
}
NavigationManager.NavigateTo("/kmslist");

View File

@@ -81,11 +81,7 @@ namespace AntSK.Pages.KmsPage
_data =await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
var km = _kmss_Repositories.GetFirst(p => p.Id == KmsId);
//使用知识库设置的参数,
_memory = iKMService.GetMemory(textPartitioningOptions:new Microsoft.KernelMemory.Configuration.TextPartitioningOptions() {
MaxTokensPerLine= km.MaxTokensPerLine,
MaxTokensPerParagraph=km.MaxTokensPerParagraph ,
OverlappingTokens=km.OverlappingTokens
});
_memory = iKMService.GetMemoryByKMS(km.Id);
}
//刷新

View File

@@ -20,7 +20,7 @@ namespace AntSK.Pages.KmsPage
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_data = await iKMService.GetDocumentByFileID(FileId);
_data = await iKMService.GetDocumentByFileID(KmsId, FileId);
}
private void NavigateBack() {

View File

@@ -33,6 +33,7 @@
{
<Card Hoverable Bordered Class="card" Actions="@(new[] {
info(()=> Info(context.Id)) ,
update(async ()=>await Update(context.Id)),
delete(async ()=>await Delete(context.Id)) ,
})" Style="max-height:247px;">
<CardMeta>
@@ -64,5 +65,6 @@
@code
{
RenderFragment info(Action clickAction) =>@<a key="info" @onclick="@clickAction">查看</a>;
RenderFragment update(Action clickAction) =>@<a key="update" @onclick="@clickAction">修改</a>;
RenderFragment delete(Action clickAction) => @<a key="delete" @onclick="@clickAction">删除</a> ;
}

View File

@@ -78,16 +78,20 @@ namespace AntSK.Pages
NavigationManager.NavigateTo($"/kms/detail/{id}");
}
private async Task Update(string id)
{
NavigationManager.NavigateTo($"/kms/add/{id}");
}
private async Task Delete(string id)
{
var _memory=_kMService.GetMemory();
var content = "删除知识库会一起删除导入的知识文档,无法还原。是否确认删除此知识库?";
var title = "删除";
var result= await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
var _memory = _kMService.GetMemoryByKMS(id);
var detailList = _kmsDetails_Repositories.GetList(p => p.KmsId == id);
foreach (var detail in detailList)
{

View File

@@ -17,23 +17,57 @@
<FormItem Label="模型描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型描述" @bind-Value="@context.ModelDescription" />
</FormItem>
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<RadioGroup @bind-Value="context.AIModelType">
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
<FormItem Label="AI类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<RadioGroup @bind-Value="context.AIType">
<Radio RadioButton Value="@(AIType.OpenAI)">OpenAI</Radio>
<Radio RadioButton Value="@(AIType.AzureOpenAI)">AzureOpenAI</Radio>
<Radio RadioButton Value="@(AIType.LLamaSharp)">LLamaSharp</Radio>
</RadioGroup>
</FormItem>
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<RadioGroup @bind-Value="context.AIModelType">
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
</RadioGroup>
</FormItem>
</FormItem>
@if (context.AIModelType == AIModelType.Embedding)
{
<FormItem Label="注意事项" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<b>请不要使用不同维度的向量模型,否则会导致无法向量存储</b>
</FormItem>
}
<FormItem Label="模型地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型地址" @bind-Value="@context.EndPoint" />
</FormItem>
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
</FormItem>
<FormItem Label="模型秘钥" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<InputPassword @bind-Value="@context.ModelKey" Placeholder="请输入模型秘钥" Size="@InputSize.Large" />
</FormItem>
@if (context.AIType == AIType.AzureOpenAI)
{
<FormItem Label="请求地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入请求地址" @bind-Value="@context.EndPoint" />
</FormItem>
<FormItem Label="部署名(模型名)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入部署名" @bind-Value="@context.ModelName" />
</FormItem>
<FormItem Label="模型秘钥" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<InputPassword @bind-Value="@context.ModelKey" Placeholder="请输入模型秘钥" Size="@InputSize.Large" />
</FormItem>
}
@if (context.AIType == AIType.OpenAI)
{
<FormItem Label="请求地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入请求地址" @bind-Value="@context.EndPoint" />
</FormItem>
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
</FormItem>
<FormItem Label="模型秘钥" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<InputPassword @bind-Value="@context.ModelKey" Placeholder="请输入模型秘钥" Size="@InputSize.Large" />
</FormItem>
}
@if (context.AIType == AIType.LLamaSharp)
{
<FormItem Label="模型路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
</FormItem>
}
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" OnClick="HandleSubmit">
保存

View File

@@ -6,6 +6,7 @@ using AntSK.Domain.Utils;
using DocumentFormat.OpenXml.InkML;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.AspNetCore.Components;
using System;
namespace AntSK.Pages.Setting.AIModel
{
@@ -33,6 +34,21 @@ namespace AntSK.Pages.Setting.AIModel
private void HandleSubmit()
{
if (_aimodels_Repositories.IsAny(p => p.AIModelType == _aiModel.AIModelType && p.EndPoint == _aiModel.EndPoint.ConvertToString() && p.ModelKey == _aiModel.ModelKey && p.ModelName == _aiModel.ModelName))
{
_ = Message.Error("模型已存在!", 2);
return;
}
if (_aiModel.AIType.IsNull())
{
_ = Message.Error("AI类型必须选择", 2);
return;
}
if (_aiModel.AIModelType.IsNull())
{
_ = Message.Error("模型类型必须选择", 2);
return;
}
if (string.IsNullOrEmpty(ModelId))
{
//新增
@@ -43,13 +59,6 @@ namespace AntSK.Pages.Setting.AIModel
_ = Message.Error("模型描述已存在!", 2);
return;
}
if (_aimodels_Repositories.IsAny(p =>p.AIModelType==_aiModel.AIModelType&& p.EndPoint == _aiModel.EndPoint&&p.ModelKey==_aiModel.ModelKey&&p.ModelName==_aiModel.ModelName))
{
_ = Message.Error("模型已存在!", 2);
return;
}
_aimodels_Repositories.Insert(_aiModel);
}
else

View File

@@ -41,7 +41,26 @@
<b>模型描述</b>
<p>@context.ModelDescription</p>
</div>
<div class="listContentItem" style="width:20%">
<div class="listContentItem" style="width:10%">
<b>AI类型</b>
<p>
@if (context.AIType == AIType.OpenAI)
{
<Tag Color="@PresetColor.Yellow.ToString()">OpenAI</Tag>
}
else if (context.AIType == AIType.AzureOpenAI)
{
<Tag Color="@PresetColor.Green.ToString()">AzureOpenAI</Tag>
}
else if (context.AIType == AIType.LLamaSharp)
{
<Tag Color="@PresetColor.Red.ToString()">LLamaSharp</Tag>
}
</p>
</div>
<div class="listContentItem" style="width:10%">
<b>模型类别</b>
<p>
@if (context.AIModelType == AIModelType.Chat)
@@ -58,10 +77,10 @@
<b>模型地址</b>
<p>@context.EndPoint</p>
</div>
<div class="listContentItem" style="width:20%">
<div class="listContentItem" style="width:10%">
<b>模型名称</b>
<p>@context.ModelName</p>
</div>
</div>
</div>
</ListItem>
</AntList>

View File

@@ -81,7 +81,6 @@ builder.Services.AddBackgroundTaskBroker().AddHandler<ImportKMSTaskReq, BackGrou
// 读取连接字符串配置
{
builder.Configuration.GetSection("DBConnection").Get<DBConnectionOption>();
builder.Configuration.GetSection("OpenAIOption").Get<OpenAIOption>();
builder.Configuration.GetSection("Login").Get<LoginOption>();
builder.Configuration.GetSection("LLamaSharp").Get<LLamaSharpOption>();
if (LLamaSharpOption.RunType.ToUpper() == "CPU")

View File

@@ -1,13 +1,18 @@
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using AntSK.Models;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.Security.Claims;
using System.Security.Principal;
namespace AntSK.Services.Auth
{
public class AntSKAuthProvider(IUsers_Repositories _users_Repositories) : AuthenticationStateProvider
public class AntSKAuthProvider(
IUsers_Repositories _users_Repositories,
ProtectedSessionStorage _protectedSessionStore
) : AuthenticationStateProvider
{
private ClaimsIdentity identity = new ClaimsIdentity();
@@ -21,6 +26,7 @@ namespace AntSK.Services.Auth
// 管理员认证成功创建用户的ClaimsIdentity
var claims = new[] { new Claim(ClaimTypes.Name, username) };
identity = new ClaimsIdentity(claims, "AntSKAdmin");
await _protectedSessionStore.SetAsync("UserSession", new UserSession() { UserName = username, Role = "AntSKAdmin" });
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
return true;
}
@@ -37,6 +43,7 @@ namespace AntSK.Services.Auth
// 用户认证成功创建用户的ClaimsIdentity
var claims = new[] { new Claim(ClaimTypes.Name, username) };
identity = new ClaimsIdentity(claims, "AntSKUser");
await _protectedSessionStore.SetAsync("UserSession", new UserSession() { UserName = username, Role = "AntSKUser" });
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
return true;
}
@@ -44,13 +51,27 @@ namespace AntSK.Services.Auth
public ClaimsPrincipal GetCurrentUser()
{
//var userSessionStorageResult =await _protectedSessionStore.GetAsync<UserSession>("UserSession");
//var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
//if (userSession.IsNotNull())
//{
// var claims = new[] { new Claim(ClaimTypes.Name, userSession.UserName) };
// identity = new ClaimsIdentity(claims, "AntSKUser");
//}
var user = new ClaimsPrincipal(identity);
return user;
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var userSessionStorageResult = await _protectedSessionStore.GetAsync<UserSession>("UserSession");
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
if (userSession.IsNotNull())
{
var claims = new[] { new Claim(ClaimTypes.Name, userSession.UserName) };
identity = new ClaimsIdentity(claims, "AntSKUser");
}
var user = new ClaimsPrincipal(identity);
return Task.FromResult(new AuthenticationState(user));
return await Task.FromResult(new AuthenticationState(user));
}
}
}

View File

@@ -46,7 +46,7 @@ namespace AntSK.Services.OpenApi
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == sk);
if (app.IsNotNull())
{
string msg= await HistorySummarize(model);
string msg= await HistorySummarize(app,model);
switch (app.Type)
{
case "chat":
@@ -90,7 +90,7 @@ namespace AntSK.Services.OpenApi
private async Task SendChatStream( HttpContext HttpContext, OpenAIStreamResult result, Apps app, string msg)
{
var _kernel = _kernelService.GetKernel();
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
if (!string.IsNullOrEmpty(app.ApiFunctionList))
@@ -146,7 +146,7 @@ namespace AntSK.Services.OpenApi
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
}
var _kernel = _kernelService.GetKernel();
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
if (!string.IsNullOrEmpty(app.ApiFunctionList))
@@ -176,8 +176,8 @@ namespace AntSK.Services.OpenApi
/// <returns></returns>
private async Task<string> SendKms(string msg, Apps app)
{
var _kernel = _kernelService.GetKernel();
var _memory = _kMService.GetMemory();
var _kernel = _kernelService.GetKernelByApp(app);
var _memory = _kMService.GetMemoryByKMS(app.KmsIdList.Split(",").FirstOrDefault());
string result = "";
//知识库问答
var filters = new List<MemoryFilter>();
@@ -216,9 +216,9 @@ namespace AntSK.Services.OpenApi
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(OpenAIModel model)
private async Task<string> HistorySummarize(Apps app,OpenAIModel model)
{
var _kernel = _kernelService.GetKernel();
var _kernel = _kernelService.GetKernelByApp(app);
StringBuilder history = new StringBuilder();
string questions = model.messages[model.messages.Count-1].content;
for(int i=0;i<model.messages.Count()-1;i++)

View File

@@ -24,9 +24,8 @@
"HeaderHeight": 48
},
"DBConnection": {
"DbType": "Sqlite", //支持PostgreSQL,Sqlite,Mysql,SqlServer,Oracle,Dm,Kdbndp,Oscar,Access,OpenGauss,QuestDB,HG,ClickHouse,GBase,Odbc,GaussDB等 注意ConnectionStrings需要安对应DB格式写
"DbType": "Sqlite",
"ConnectionStrings": "Data Source=AntSK.db;"
//"ConnectionStrings": "Host=;Port=;Database=antsk;Username=;Password="
},
"OpenAIOption": {
"EndPoint": "http://localhost:5000/llama/",
@@ -35,12 +34,12 @@
"EmbeddingModel": "text-embedding-ada-002"
},
"KernelMemory": {
"VectorDb": "Disk", //Postgres,Disk,Memory 三选一
"VectorDb": "Disk",
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
"TableNamePrefix": "km-"
},
"LLamaSharp": {
"RunType": "GPU", //CPU,GPU 两选一
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
},
@@ -50,7 +49,7 @@
},
"BackgroundTaskBroker": {
"ImportKMSTask": {
"WorkerCount": 1 //导入队列数
"WorkerCount": 1
}
}
}