Compare commits

..

69 Commits
0.1.8 ... 0.2.2

Author SHA1 Message Date
zeyu xu
0e754b4732 fix 注释 2024-03-19 22:09:50 +08:00
zeyu xu
48a9fcfabf fix 修改样式,提取KMOption 2024-03-19 21:53:49 +08:00
zyxucp
7ae2b9ac3b Merge pull request #33 from ElderJames/feat/chat-file
support embeding files in chat
2024-03-19 21:32:21 +08:00
zeyu xu
a6bfe3c69b Merge branch 'main' of github.com:AIDotNet/AntSK 2024-03-19 20:40:24 +08:00
zeyu xu
6b146f4750 忽略本地目录 2024-03-19 20:40:19 +08:00
zeyu xu
1387e716d1 忽略tmp-memory-vectors/ 2024-03-19 20:39:31 +08:00
James Yeung
fadc350047 support embeding files in chat 2024-03-19 16:39:27 +08:00
zyxucp
8af4994a7d Merge pull request #32 from ElderJames/feat/DashScope
Add DashScope support for Kernal Memory
2024-03-19 15:54:05 +08:00
zyxucp
b505b61bfe fix 增加ask提示词 2024-03-19 15:36:01 +08:00
James Yeung
5d086c9383 Add DashScope support for Kernal Memory
Support DashScope
2024-03-19 13:35:45 +08:00
zeyu xu
dd0e367dc8 fix 增加默认端口5000 2024-03-19 00:06:36 +08:00
zyxucp
7110eea912 fix 修改注释错误 2024-03-18 17:26:40 +08:00
zyxucp
154e67ef98 fix 判断无插件不走function call 2024-03-18 17:17:03 +08:00
zyxucp
4a04423373 add swagger token 2024-03-18 16:04:55 +08:00
zyxucp
470ea50ebb update readme.en 2024-03-17 23:53:47 +08:00
zyxucp
2a30f3f221 fix 修改post提示词 2024-03-17 20:07:53 +08:00
zyxucp
315f58fdba fix 修改api插件 2024-03-17 20:04:48 +08:00
zyxucp
f027682175 add post的Function call 2024-03-17 17:23:50 +08:00
zyxucp
2618dffcd6 fix 修复知识库ID错误导致搜索不到的问题 2024-03-17 15:56:59 +08:00
zyxucp
ee42d5870b fix 增加清空导入插件功能 2024-03-17 12:05:32 +08:00
zyxucp
27500b1e08 fix 修改错误命名空间和类 2024-03-17 11:45:24 +08:00
zyxucp
4d71a98724 fix 修改Function Call注释获取不到的问题 2024-03-17 11:30:02 +08:00
zyxucp
b8ba0ab391 Merge pull request #30 from wmchuang/feature/km_disk
Feature/km disk
Thank you very much for your contribution
2024-03-16 21:24:08 +08:00
王闯
b589612913 feat: 解决disk模式下重启 向量文件读取不到的问题 2024-03-16 17:45:45 +08:00
王闯
ec4b440469 feat: 调整知识库搜索 没结果时直接返回 2024-03-16 17:16:29 +08:00
zyxucp
adbecb3b25 update 更新docker compose 2024-03-16 00:23:16 +08:00
zyxucp
277aacc34d add 动态添加dll 2024-03-15 23:53:53 +08:00
zyxucp
dba98f7968 add 增加上传dll功能 2024-03-15 22:55:23 +08:00
zyxucp
a2b0f3f3c2 add 插件导入 2024-03-15 22:45:36 +08:00
zyxucp
b5e527afdb add 增加函数列表 2024-03-15 22:33:39 +08:00
zyxucp
e84b05a39b fix 修改普通特性 2024-03-15 22:16:07 +08:00
zyxucp
631c563e71 fix 修改为普通特性,避免上传DLL需要引用特殊特性 2024-03-15 22:15:02 +08:00
zyxucp
47b304e46f update 升级SK到1.6.2 2024-03-15 22:08:22 +08:00
zyxucp
27ccfc5e88 add 增加分享使用 2024-03-15 21:57:21 +08:00
zyxucp
69441167d3 fix 修改高度自适应 2024-03-15 21:50:55 +08:00
zyxucp
4b97594217 fix 调整会话总结 2024-03-15 21:15:28 +08:00
zyxucp
9ee601f88c fix 首页调整 2024-03-15 21:12:31 +08:00
zyxucp
75b1a299e3 add 限制移动端屏幕缩放 2024-03-15 21:07:59 +08:00
zyxucp
2613c463a1 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-15 14:45:15 +08:00
zyxucp
78e0350d36 add 首页调整 2024-03-15 14:39:13 +08:00
zyxucp
bc2425fc3f Update README.md 2024-03-15 14:18:15 +08:00
zyxucp
3e861bc72f Update README.md 2024-03-15 13:01:35 +08:00
zyxucp
b41c464753 fix 暂时取消星火的会话拆分 2024-03-14 16:58:19 +08:00
zyxucp
2817275091 fix 暂时取消注册时间函数 2024-03-14 12:57:28 +08:00
zyxucp
e76b0cf326 fix 调整目录结构 2024-03-14 12:24:09 +08:00
zyxucp
cf34103e15 update docker-compose.yml 2024-03-14 12:07:17 +08:00
zyxucp
1588fd7d7a add 增加ErrorBoundary全局异常 2024-03-14 11:32:42 +08:00
zyxucp
4507ccde6c margin 2024-03-14 10:03:40 +08:00
zyxucp
73fffd766f fix 修复LLamaSharp给一个默认下载路径 2024-03-14 10:02:15 +08:00
zyxucp
e529146c5b fix 修改LLamaSharp文件夹给一个默认路径 2024-03-14 09:59:51 +08:00
zyxucp
7050e52009 Merge pull request #28 from ElderJames/main
Fix DI and OpenAI function calling
2024-03-13 22:25:45 +08:00
James Yeung
4f3238c4f6 Fix DI and OpenAI function calling 2024-03-13 22:22:22 +08:00
zyxucp
b7d27c5d50 fix 修复bug 2024-03-13 22:11:35 +08:00
zyxucp
fe94aa0564 fix 修复修改自身重复问题 2024-03-13 20:59:14 +08:00
zyxucp
ae60a9aced Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-13 12:13:56 +08:00
zyxucp
4f686b0871 fix 修复按钮停止不了的问题 2024-03-13 10:23:52 +08:00
zyxucp
c61840b7e8 Update README.md 2024-03-13 10:17:50 +08:00
zyxucp
9adce95367 fix appsettings.json示例 2024-03-13 10:10:52 +08:00
zyxucp
eef943458e fix 模型选择只能选gguf,修复搜索空指针页面 2024-03-13 10:02:00 +08:00
zyxucp
f5c80689d4 add 增加模型列表 自动下载模型 2024-03-13 00:52:46 +08:00
zyxucp
5eaee3130a add 增加模型下载页面 2024-03-13 00:10:30 +08:00
zyxucp
5846473f28 Merge branch 'main' of https://github.com/xuzeyu91/AntSK 2024-03-12 23:20:02 +08:00
zyxucp
94c019b484 Merge pull request #25 from ElderJames/model-download
support model download and file list
2024-03-12 22:51:15 +08:00
zyxucp
7e1140c022 add 增加下载页面 2024-03-12 22:50:06 +08:00
James Yeung
ea9044719a support model download and file list 2024-03-12 22:41:12 +08:00
zyxucp
8a96095448 add 增加模型下载地址 2024-03-12 21:57:34 +08:00
zyxucp
fcc8f8751b add 修改对外接口授权添加Bearer 2024-03-12 21:34:58 +08:00
zyxucp
af09ae7c3e Update docker-compose.simple.yml 2024-03-12 10:22:39 +08:00
zyxucp
e8e6a36d7b Update docker-compose.yml 2024-03-12 10:22:12 +08:00
105 changed files with 2384 additions and 894 deletions

3
.gitignore vendored
View File

@@ -337,6 +337,9 @@ ASALocalRun/
/AntSK/appsettings.Development.json
/AntSK.db
**/tmp-memory-files/*
**/tmp-memory-vectors/*
/src/AntSK/AntSK.db
/src/AntSK/appsettings.Development.json
/src/AntSK.db
/src/AntSK/llama_models
/src/AntSK/AntSK.xml

View File

@@ -1,41 +1,42 @@
[简体中文](./README.md) | English
# AntSK
## Based on AI knowledge base/agent created by Net8+AntBlazor+SemanticKernel
## An AI knowledge base/intelligent agent built with .Net 8+AntBlazor+SemanticKernel
## Core functions
## Core Features
- **Semantic Kernel**: It uses advanced natural language processing technology to accurately understand, process and respond to complex semantic queries, and provides users with accurate information retrieval and recommendation services.
- **Semantic Kernel**: Utilizes advanced natural language processing technologies to accurately understand, process, and respond to complex semantic queries, providing users with precise information retrieval and recommendation services.
- **Kernel Memory**: It has the ability to continuously learn and store knowledge points. AntSK has a long-term memory function to accumulate experience and provide a more personalized interactive experience.
- **Kernel Memory**: Capable of continuous learning and knowledge storage, AntSK has a long-term memory function, accumulating experience to offer more personalized interaction experiences.
- **Knowledge base**: Knowledge base documents can be created by importing knowledge base documents (Word, PDF, Excel, Txt, Markdown, Json, PPT) and other forms.
- **Knowledge base**: Import knowledge into the database through documents (Word, PDF, Excel, Txt, Markdown, Json, PPT) and manage knowledge base documents.
- **API plug-in system**: an open API plug-in system that allows third-party developers or service providers to easily integrate their services into AntSK and continuously enhance application functions.
- **GPTs Generation**The platform supports the creation of personalized GPT models, try building your own GPT model.
- **Online search**: AntSK can obtain the latest information in real time to ensure that the information received by users is always the most timely and relevant.
- **API Interface Release**: Internal functions are provided as APIs for developers to integrate AntSK into other applications, enhancing application intelligence.
- **GPTs generation**: This platform supports the creation of personalized GPT models and attempts to build your own GPT models.
- **API Plugin System**: An open API plugin system allows third-party developers or service providers to easily integrate their services into AntSK, continuously enhancing application functions.
- **.Net Plugin System**: An open dll plugin system allows third-party developers or service providers to integrate their business functions into AntSK by generating dlls with the standard format codes, continuously enhancing application functions.
- **API interface publishing**: internal functions are provided externally in the form of API, so that developers can easily translate Xzy AntSK KnowledgeBase is integrated into other applications to enhance application intelligence.
- **Model management**: Adapt and manage different models from different vendors.
- **Internet Search**: AntSK can retrieve the latest information in real-time, ensuring that the information users receive is always timely and relevant.
- **Model management**: Adapts and manages different models from various manufacturers. It also supports offline running of models in 'gguf' format supported by llama.cpp.
- **National Information Creation**: AntSK supports domestic models and databases, and can operate under information creation conditions.
@@ -43,11 +44,11 @@
AntSK is applicable to a variety of business scenarios, such as:
AntSK is suitable for various business scenarios, such as:
- Enterprise level knowledge management system
- Corporate knowledge management systems
- Automatic customer service and chat robot
- Automated customer service and chatbots
- Enterprise Search Engine
@@ -57,7 +58,7 @@ AntSK is applicable to a variety of business scenarios, such as:
- Education and online learning platform
- Other interesting AI Apps
- Other interesting AI applications
@@ -115,25 +116,71 @@ Let's see the effect
## How do I get started?
Login is the default login account and password
Here I am using Postgres as a data and vector store, because the Semantic Kernel and Kernel Memory both support it, though you can switch to others.
Here I use Postgres as data storage and vector storage, because both the Semantic Kernel and Kernel Memory support it. Of course, you can switch to other ones.
The model by default supports local models in 'gguf' format from openai, azure openai, and llama. If you need to use other models, you can integrate them using the one-api.
The model supports openai by default. If you need to use azure openai and need to adjust the dependency injection of SK, you can also use one api for integration.
The login configuration in the configuration file is the default account and password.
The following configuration files need to be configured
The following configuration files are needed:
## 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
A appsettings.json for the pg version and a simplified version (Sqlite+disk) docker-compose.simple.yml are provided.
Download docker-compose.yml from the project root directory, then place the configuration file appsettings.json in the same directory,
The pg image has already been prepared. You can modify the default account and password in the docker-compose.yml, and then your appsettings.json database connection needs to be consistent.
Then you can enter the directory and execute
```
docker compose up - d
```
To start AntSK
to start AntSK.
Some meanings of configuration files
How to mount local models and model download directories in docker
```
# Non-host version, does not use local proxy
version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
ports:
- 5000:5000
networks:
- antsk
depends_on:
- antskpg
restart: always
environment:
- ASPNETCORE_URLS=http://*:5000
volumes:
- ./appsettings.json:/app/appsettings.json # local configuration file must be placed in the same directory
- D://model:/app/model
networks:
antsk:
```
Using this as an example, the meaning is to mount the local folder D://model from Windows into the container /app/model. If so, your appsettings.json model directory should be configured as
```
model/xxx.gguf
```
Some meanings of the configuration file
// (The rest of the information is omitted as it's unnecessary for the translation example context.)
Solving the missing style issue:
Execute under AntSK/src/AntSK:
```
dotnet clean
dotnet build
dotnet publish "AntSK.csproj"
```
Then go to AntSK/src/AntSK/bin/Release/net8.0/publish
```
dotnet AntSK.dll
```
```
@@ -142,12 +189,6 @@ Some meanings of configuration files
"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=",
@@ -156,7 +197,8 @@ Some meanings of configuration files
"LLamaSharp": {
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"FileDirectory": "D:\\Code\\AI\\AntBlazor\\model\\"
},
"Login": {
"User": "admin",
@@ -176,10 +218,6 @@ Some meanings of configuration files
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

View File

@@ -16,12 +16,14 @@
- **API插件系统**开放式API插件系统允许第三方开发者或服务商轻松将其服务集成到AntSK不断增强应用功能。
- **.Net插件系统(规划中)**开放式dll插件系统允许第三方开发者或服务商轻松将其业务功能通过标准格式的代码生成dll后集成到AntSK不断增强应用功能。
- **.Net插件系统**开放式dll插件系统允许第三方开发者或服务商轻松将其业务功能通过标准格式的代码生成dll后集成到AntSK不断增强应用功能。
- **联网搜索**AntSK实时获取最新信息确保用户接受到的资料总是最及时、最相关的。
- **模型管理**适配和管理集成不同厂商的不同模型。并且支持llama.cpp所支持的gguf类型的模型离线运行
- **国产信创**AntSK支持国产模型和国产数据库可以在信创条件下运行
## 应用场景
AntSK 适用于多种业务场景,例如:
@@ -85,7 +87,7 @@ docker-compose up -d
```
来启动AntSK
## 如何在docker中挂载本地模型
## 如何在docker中挂载本地模型,和模型下载的目录
```
# 非 host 版本, 不使用本机代理
version: '3.8'
@@ -128,7 +130,8 @@ model/xxx.gguf
"LLamaSharp": {
"RunType": "GPU",
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf"
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"FileDirectory": "D:\\Code\\AI\\AntBlazor\\model\\"
},
"Login": {
"User": "admin",
@@ -156,8 +159,10 @@ LLamaSharp.RunType
LLamaSharp.Chat
//本地向量模型的模型路径 注意区分linux和windows盘符不同
LLamaSharp.Embedding
//默认管理员账号密码
//本地模型路径用于在选择llama时可以快速选择目录下的模型以及保存下载的模型
LLamaSharp.FileDirectory
//默认管理员账号密码
Login
//导入异步处理的线程数使用在线API可以高一点本地模型建议1 否则容易内存溢出崩掉
BackgroundTaskBroker.ImportKMSTask.WorkerCount

View File

@@ -3,7 +3,7 @@ version: '3.8'
services:
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.7
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
ports:
- 5000:5000
networks:

View File

@@ -18,7 +18,7 @@ services:
- ./pg/data:/var/lib/postgresql/data
antsk:
container_name: antsk
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.7
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
ports:
- 5000:5000
networks:

View File

@@ -13,17 +13,17 @@
<PackageReference Include="AutoMapper" Version="8.1.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Markdig" Version="0.35.0" />
<PackageReference Include="Markdig" Version="0.36.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.145" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
<PackageReference Include="RestSharp" Version="110.2.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.4.0" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.4.0" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.4.0-alpha" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.32.240308.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.32.240308.1" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.6.2" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.6.2" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.6.2-alpha" />
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.34.240313.1" />
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.34.240313.1" />
<PackageReference Include="LLamaSharp" Version="0.10.0" />
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.10.0" />

View File

@@ -48,6 +48,36 @@
<param name="value"></param>
<returns></returns>
</member>
<member name="T:AntSK.Domain.Domain.Model.Enum.AIType">
<summary>
AI类型
</summary>
</member>
<member name="T:AntSK.Domain.Domain.Model.Enum.AIModelType">
<summary>
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.MessageInfo.IsSend">
<summary>
发送是true 接收是false
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.PageIndex">
<summary>
当前页从1开始
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.PageSize">
<summary>
每页数量
</summary>
</member>
<member name="P:AntSK.Domain.Domain.Model.PageList`1.TotalCount">
<summary>
总数
</summary>
</member>
<member name="F:AntSK.Domain.Domain.Other.LLamaConfig.dicLLamaWeights">
<summary>
避免模型重复加载,本地缓存
@@ -82,6 +112,20 @@
<param name="app"></param>
<param name="_kernel"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.ImportApiFunction(AntSK.Domain.Repositories.Apps,System.Collections.Generic.List{Microsoft.SemanticKernel.KernelFunction})">
<summary>
导入API插件
</summary>
<param name="app"></param>
<param name="functions"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.ImportNativeFunction(AntSK.Domain.Repositories.Apps,System.Collections.Generic.List{Microsoft.SemanticKernel.KernelFunction})">
<summary>
导入原生插件
</summary>
<param name="app"></param>
<param name="functions"></param>
</member>
<member name="M:AntSK.Domain.Domain.Service.KernelService.RegisterPluginsWithKernel(Microsoft.SemanticKernel.Kernel)">
<summary>
注册默认插件
@@ -97,61 +141,6 @@
<param name="history"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTOList``1(System.Object)">
<summary>
Entity集合转DTO集合
</summary>
<typeparam name="T"></typeparam>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTO``1(System.Object)">
<summary>
Entity转DTO
</summary>
<typeparam name="T"></typeparam>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Map.MapperExtend.MapTo``1(System.Object,``0)">
<summary>
给已有对象map,适合update场景如需过滤空值需要在AutoMapProfile 设置
</summary>
<typeparam name="T"></typeparam>
<param name="self"></param>
<param name="result"></param>
<returns></returns>
</member>
<member name="T:AntSK.Domain.Model.Enum.AIType">
<summary>
AI类型
</summary>
</member>
<member name="T:AntSK.Domain.Model.Enum.AIModelType">
<summary>
模型类型
</summary>
</member>
<member name="P:AntSK.Domain.Model.MessageInfo.IsSend">
<summary>
发送是true 接收是false
</summary>
</member>
<member name="P:AntSK.Domain.Model.PageList`1.PageIndex">
<summary>
当前页从1开始
</summary>
</member>
<member name="P:AntSK.Domain.Model.PageList`1.PageSize">
<summary>
每页数量
</summary>
</member>
<member name="P:AntSK.Domain.Model.PageList`1.TotalCount">
<summary>
总数
</summary>
</member>
<member name="P:AntSK.Domain.Options.DBConnectionOption.DbType">
<summary>
sqlite连接字符串
@@ -242,6 +231,11 @@
会话模型ID
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.EmbeddingModelID">
<summary>
Embedding 模型Id
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Apps.Temperature">
<summary>
温度
@@ -272,6 +266,11 @@
API调用秘钥
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Funs.Path">
<summary>
接口描述
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.KmsDetails.FileName">
<summary>
文件名称
@@ -744,6 +743,13 @@
<param name="stream"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Utils.ConvertUtils.ToQueryString(System.Collections.Generic.Dictionary{System.String,System.String})">
<summary>
json参数转化querystring参数
</summary>
<param name="parameters"></param>
<returns></returns>
</member>
<member name="M:AntSK.Domain.Utils.RepoFiles.SamplePluginsPath">
<summary>
Scan the local folders from the repo, looking for "samples/plugins" folder.

View File

@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Dto
{
public class RelevantSource
{
public string SourceName { get; set; }
public string Text { get; set; }
public float Relevance { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Repositories;
using Microsoft.SemanticKernel;
using System;
@@ -13,6 +13,6 @@ namespace AntSK.Domain.Domain.Interface
{
IAsyncEnumerable<StreamingKernelContent> SendChatByAppAsync(Apps app, string questions, string history);
IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, List<RelevantSource> relevantSources = null);
IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, string filePath, List<RelevantSource> relevantSources = null);
}
}
}

View File

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

View File

@@ -1,11 +1,24 @@
using AntSK.Domain.Domain.Dto;
using AntDesign;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Repositories;
using Microsoft.KernelMemory;
namespace AntSK.Domain.Domain.Interface
{
public interface IKMService
{
MemoryServerless GetMemory(Apps app);
MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null);
Task<List<KMFile>> GetDocumentByFileID(string kmsid, string fileid);
Task<List<KMFile>> GetDocumentByFileID(string kmsId, string fileId);
Task<List<RelevantSource>> GetRelevantSourceList(string kmsIdListStr, string msg);
List<UploadFileItem> FileList { get; }
bool BeforeUpload(UploadFileItem file);
void OnSingleCompleted(UploadInfo fileinfo);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Model.Constant
{
public class KmsConstantcs
{
public const string KmsIdTag = "kmsid";
public const string KmsIndex = "kms";
public const string KmsSearchNull="知识库未搜索到相关内容";
}
}

View File

@@ -1,15 +1,14 @@
namespace AntSK.Domain.Domain.Dto
namespace AntSK.Domain.Domain.Model.Dto
{
public class KMFile
{
public string DocumentId { get; set; }
public string Text { get; set; }
public string Url { get; set; }
public string? Url { get; set; }
public string LastUpdate { get; set; }
public string Schema { get; set; }
public string File { get; set; }
}
}
}

View File

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

View File

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

View File

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

namespace AntSK.Domain.Domain.Model.Dto
{
public class RelevantSource
{
public string SourceName { get; set; }
public string Text { get; set; }
public float Relevance { get; set; }
public override string ToString()
{
return $"[file:{SourceName};Relevance:{(Relevance * 100):F2}%]:{Text}";
}
}
}

View File

@@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations;
namespace AntSK.Domain.Domain.Model.Enum
{
/// <summary>
/// AI类型
/// </summary>
public enum AIType
{
[Display(Name = "Open AI")]
OpenAI = 1,
[Display(Name = "Azure Open AI")]
AzureOpenAI = 2,
[Display(Name = "LLama本地模型")]
LLamaSharp = 3,
[Display(Name = "星火大模型")]
SparkDesk = 4,
[Display(Name = "灵积大模型")]
DashScope = 5,
[Display(Name = "模拟输出")]
Mock = 100,
}
/// <summary>
/// 模型类型
/// </summary>
public enum AIModelType
{
Chat = 1,
Embedding = 2,
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
namespace AntSK.Domain.Model
namespace AntSK.Domain.Domain.Model
{
public class MessageInfo
{
@@ -10,7 +10,11 @@
/// 发送是true 接收是false
/// </summary>
public bool IsSend { get; set; } = false;
public DateTime CreateTime { get; set; }
public string? FilePath { get; set; }
public string? FileName { get; set; }
}
}
}

View File

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

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Model.hfmirror
{
public class HfModel
{
public List<HfModels> models { get; set; }
public int numItemsPerPage { get; set; }
public int numTotalItems { get; set; }
public int pageIndex { get; set; }
}
public class HfModels
{
public string Author { get; set; }
public HfAuthorData AuthorData { get; set; }
public int Downloads { get; set; }
public bool Gated { get; set; }
public string Id { get; set; }
public DateTime LastModified { get; set; }
public int Likes { get; set; }
public string PipelineTag { get; set; }
public bool Private { get; set; }
public string RepoType { get; set; }
public bool IsLikedByUser { get; set; }
}
public class HfAuthorData
{
public string AvatarUrl { get; set; }
public string Fullname { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public bool IsHf { get; set; }
public bool IsEnterprise { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntSK.Domain.Domain.Model.hfmirror
{
public class HfModelDetail
{
public string Name { get; set; }
public string Size { get; set; }
public string Path { get; set; }
public string Time { get; set; }
}
}

View File

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

View File

@@ -3,15 +3,14 @@ using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Repositories;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AntSK.Domain.Utils;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Domain.Model.Constant;
using DocumentFormat.OpenXml.Drawing;
using System.Reflection.Metadata;
using Microsoft.KernelMemory;
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Dto;
using System.Collections.Generic;
using Markdig;
namespace AntSK.Domain.Domain.Service
@@ -40,7 +39,7 @@ namespace AntSK.Domain.Domain.Service
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100;//存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
if (!string.IsNullOrEmpty(app.ApiFunctionList)|| !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
if (!string.IsNullOrEmpty(app.ApiFunctionList) || !string.IsNullOrEmpty(app.NativeFunctionList))//这里还需要加上本地插件的
{
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
@@ -53,50 +52,51 @@ namespace AntSK.Domain.Domain.Service
}
}
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, List<RelevantSource> relevantSources = null)
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, string history, string filePath, List<RelevantSource> relevantSources = null)
{
var relevantSourceList = await _kMService.GetRelevantSourceList(app.KmsIdList, questions);
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)
if (!string.IsNullOrWhiteSpace(filePath))
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
string dataMsg = "";
if (xlresult != null)
{
foreach (var item in xlresult.Results)
{
foreach (var part in item.Partitions)
{
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance * 100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
var memory = _kMService.GetMemory(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);
if (relevantSources.IsNotNull())
{
string sourceName = item.SourceName;
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == item.SourceName);
if (fileDetail.IsNotNull())
{
sourceName = fileDetail.FileName;
}
relevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = Markdown.ToHtml(part.Text), Relevance = part.Relevance });
}
}
var filters = new MemoryFilter().ByTag(KmsConstantcs.KmsIdTag, app.Id);
var searchResult = await memory.SearchAsync(questions, index: KmsConstantcs.KmsIndex, 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
})));
}
var dataMsg = new StringBuilder();
if (relevantSourceList.Any())
{
relevantSources?.AddRange(relevantSourceList);
foreach (var item in relevantSources)
{
dataMsg.AppendLine(item.ToString());
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = _kernel.InvokeStreamingAsync(function: jsonFun,
arguments: new KernelArguments() { ["doc"] = dataMsg, ["history"] = history, ["questions"] = questions });
MessageInfo info = null;
await foreach (var content in chatResult)
{
yield return content;
}
}
else
{
yield return new StreamingTextContent(KmsConstantcs.KmsSearchNull);
}
}
}
}

View File

@@ -1,17 +1,24 @@
using System.Reflection;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Loader;
using System.Xml;
using AntSK.Domain.Common;
using AntSK.Domain.Utils;
using System.Text.RegularExpressions;
using Microsoft.SemanticKernel;
using HtmlAgilityPack;
using System.Collections.Generic;
namespace AntSK.Domain.Domain.Service
{
public class FunctionService
{
private readonly Dictionary<string, Func<object[], object>> _methodCache;
private readonly Dictionary<string, MethodInfo> _methodCache;
private readonly Dictionary<string, (string Description, (Type ParameterType, string Description) ReturnType, (string ParameterName, Type ParameterType, string Description)[] Parameters)> _methodInfos;
private readonly IServiceProvider _serviceProvider;
private readonly Assembly[] _assemblies;
private Assembly[] _assemblies;
private readonly AssemblyLoadContext loadContext;
public FunctionService(IServiceProvider serviceProvider, Assembly[] assemblies)
{
@@ -19,9 +26,10 @@ namespace AntSK.Domain.Domain.Service
_methodInfos = [];
_serviceProvider = serviceProvider;
_assemblies = assemblies;
loadContext = new AssemblyLoadContext("AntSKLoadContext", true);
}
public Dictionary<string, Func<object[], object>> Functions => _methodCache;
public Dictionary<string, MethodInfo> Functions => _methodCache;
public Dictionary<string, (string Description, (Type ParameterType, string Description) ReturnType, (string ParameterName, Type ParameterType, string Description)[] Parameters)> MethodInfos => _methodInfos;
/// <summary>
@@ -39,7 +47,26 @@ namespace AntSK.Domain.Domain.Service
// 从缓存中获取标记了ActionAttribute的方法
foreach (var type in assembly.GetTypes())
{
markedMethods.AddRange(type.GetMethods().Where(m => m.GetCustomAttributes(typeof(AntSkFunctionAttribute), true).Length > 0));
markedMethods.AddRange(type.GetMethods().Where(m =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description.Contains( "AntSK");
}));
}
}
//动态加载部分
var loadedAssemblies = loadContext.Assemblies.ToList();
foreach (var assembly in loadedAssemblies)
{
// 从缓存中获取标记了ActionAttribute的方法
foreach (var type in assembly.GetTypes())
{
markedMethods.AddRange(type.GetMethods().Where(m =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description.Contains("AntSK");
}));
}
}
@@ -47,23 +74,49 @@ namespace AntSK.Domain.Domain.Service
foreach (var method in markedMethods)
{
var key = $"{method.DeclaringType.Assembly.GetName().Name}_{method.DeclaringType.Name}_{method.Name}";
_methodCache.TryAdd(key, arguments =>
{
var instance = _serviceProvider.GetService(method.DeclaringType);
return method.Invoke(instance, arguments);
});
var xmlCommentHelper = new XmlCommentHelper();
xmlCommentHelper.LoadAll();
var description = xmlCommentHelper.GetMethodComment(method);
var dict = xmlCommentHelper.GetParameterComments(method);
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType, dict[x.Name])).ToArray();
var returnType = xmlCommentHelper.GetMethodReturnComment(method);
string pattern = "[^a-zA-Z0-9_]";
// 使用 '-' 替换非ASCII的正则表达式的字符
key = Regex.Replace(key, pattern, "_");
_methodCache.TryAdd(key, method);
var description= method.GetCustomAttribute<DescriptionAttribute>().Description.ConvertToString().Replace("AntSK:","");
var returnType = method.ReturnParameter.GetCustomAttribute<DescriptionAttribute>().Description.ConvertToString();
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType,x.GetCustomAttribute<DescriptionAttribute>()?.Description)).ToArray();
// 假设 _methodInfos 是一个已经定义好的字典,用来保存方法的相关信息
_methodInfos.TryAdd(key, (description, (method.ReflectedType, returnType), parameters));
}
}
public void FuncLoad(string pluginPath)
{
try
{
if (File.Exists(pluginPath))
{
string directory = Path.GetDirectoryName(pluginPath);
string fileName = Path.GetFileName(pluginPath);
var resolver = new AssemblyDependencyResolver(directory);
// Create a custom AssemblyLoadContext
loadContext.Resolving += (context, assemblyName) =>
{
string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return context.LoadFromAssemblyPath(assemblyPath);
}
return null;
};
// Load your assembly
Assembly pluginAssembly = loadContext.LoadFromAssemblyPath(pluginPath);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
}
}

View File

@@ -1,6 +1,7 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Constant;
using AntSK.Domain.Repositories;
using Microsoft.KernelMemory;
@@ -29,8 +30,8 @@ namespace AntSK.Domain.Domain.Service
{
var importResult = _memory.ImportDocumentAsync(new Document(fileid)
.AddFile(req.FilePath)
.AddTag("kmsid", req.KmsId)
, index: "kms").Result;
.AddTag(KmsConstantcs.KmsIdTag, req.KmsId)
, index: KmsConstantcs.KmsIndex).Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(km.Id, fileid).Result;
string fileGuidName = Path.GetFileName(req.FilePath);
@@ -43,8 +44,8 @@ namespace AntSK.Domain.Domain.Service
case ImportType.Url:
{
//导入url
var importResult = _memory.ImportWebPageAsync(req.Url, fileid, new TagCollection() { { "kmsid", req.KmsId } }
, index: "kms").Result;
var importResult = _memory.ImportWebPageAsync(req.Url, fileid, new TagCollection() { { KmsConstantcs.KmsIdTag, req.KmsId } }
, index: KmsConstantcs.KmsIndex).Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(km.Id, fileid).Result;
req.KmsDetail.Url = req.Url;
@@ -54,8 +55,8 @@ namespace AntSK.Domain.Domain.Service
case ImportType.Text:
//导入文本
{
var importResult = _memory.ImportTextAsync(req.Text, fileid, new TagCollection() { { "kmsid", req.KmsId } }
, index: "kms").Result;
var importResult = _memory.ImportTextAsync(req.Text, fileid, new TagCollection() { { KmsConstantcs.KmsIdTag, req.KmsId } }
, index: KmsConstantcs.KmsIndex).Result;
//查询文档数量
var docTextList = _kMService.GetDocumentByFileID(km.Id, fileid).Result;
req.KmsDetail.Url = req.Url;

View File

@@ -1,29 +1,75 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Dto;
using AntDesign;
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Constant;
using AntSK.Domain.Domain.Model.Dto;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using DocumentFormat.OpenXml.Drawing.Diagrams;
using LLama;
using LLamaSharp.KernelMemory;
using Markdig;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration;
using Microsoft.KernelMemory;
using Microsoft.KernelMemory.Configuration;
using Microsoft.KernelMemory.ContentStorage.DevTools;
using Microsoft.KernelMemory.FileSystem.DevTools;
using Microsoft.KernelMemory.MemoryStorage;
using Microsoft.KernelMemory.MemoryStorage.DevTools;
using Microsoft.KernelMemory.Postgres;
namespace AntSK.Domain.Domain.Service
{
[ServiceDescription(typeof(IKMService), ServiceLifetime.Scoped)]
public class KMService(
IConfiguration _config,
IKmss_Repositories _kmss_Repositories,
IAIModels_Repositories _aIModels_Repositories
) : IKMService
IKmss_Repositories _kmss_Repositories,
IAIModels_Repositories _aIModels_Repositories,
IMessageService? _message
) : IKMService
{
private MemoryServerless _memory;
private List<UploadFileItem> _fileList = [];
public List<UploadFileItem> FileList => _fileList;
public MemoryServerless GetMemory(Apps app)
{
var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);
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
{
MaxAskPromptSize = 2048,
MaxMatchesCount = 3,
AnswerTokens = 1000,
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
var memoryBuild = new KernelMemoryBuilder()
.WithSearchClientConfig(searchClientConfig)
//.WithCustomTextPartitioningOptions(new TextPartitioningOptions
//{
// MaxTokensPerLine = app.MaxTokensPerLine,
// MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
// OverlappingTokens = kms.OverlappingTokens
//})
;
//加载会话模型
WithTextGenerationByAIType(memoryBuild, chatModel, chatHttpClient);
//加载向量模型
WithTextEmbeddingGenerationByAIType(memoryBuild, embedModel, embeddingHttpClient);
//加载向量库
WithMemoryDbByVectorDB(memoryBuild);
_memory = memoryBuild.Build<MemoryServerless>();
return _memory;
}
public MemoryServerless GetMemoryByKMS(string kmsID, SearchClientConfig searchClientConfig = null)
{
//if (_memory.IsNull())
@@ -45,24 +91,24 @@ namespace AntSK.Domain.Domain.Service
MaxAskPromptSize = 2048,
MaxMatchesCount = 3,
AnswerTokens = 1000,
EmptyAnswer = "知识库未搜索到相关内容"
EmptyAnswer = KmsConstantcs.KmsSearchNull
};
}
var memoryBuild = new KernelMemoryBuilder()
.WithSearchClientConfig(searchClientConfig)
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
{
MaxTokensPerLine = kms.MaxTokensPerLine,
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
OverlappingTokens = kms.OverlappingTokens
});
//加载huihu 模型
.WithSearchClientConfig(searchClientConfig)
.WithCustomTextPartitioningOptions(new TextPartitioningOptions
{
MaxTokensPerLine = kms.MaxTokensPerLine,
MaxTokensPerParagraph = kms.MaxTokensPerParagraph,
OverlappingTokens = kms.OverlappingTokens
});
//加载会话模型
WithTextGenerationByAIType(memoryBuild, chatModel, chatHttpClient);
//加载向量模型
WithTextEmbeddingGenerationByAIType(memoryBuild, embedModel, embeddingHttpClient);
//加载向量库
WithMemoryDbByVectorDB(memoryBuild, _config);
WithMemoryDbByVectorDB(memoryBuild);
_memory = memoryBuild.Build<MemoryServerless>();
return _memory;
@@ -70,10 +116,10 @@ namespace AntSK.Domain.Domain.Service
//else {
// return _memory;
//}
}
private void WithTextEmbeddingGenerationByAIType(IKernelMemoryBuilder memory, AIModels embedModel, HttpClient embeddingHttpClient)
private void WithTextEmbeddingGenerationByAIType(IKernelMemoryBuilder memory, AIModels embedModel,
HttpClient embeddingHttpClient)
{
switch (embedModel.AIType)
{
@@ -84,6 +130,7 @@ namespace AntSK.Domain.Domain.Service
EmbeddingModel = embedModel.ModelName
}, null, false, embeddingHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
memory.WithAzureOpenAITextEmbeddingGeneration(new AzureOpenAIConfig()
{
@@ -94,15 +141,20 @@ namespace AntSK.Domain.Domain.Service
APIType = AzureOpenAIConfig.APITypes.EmbeddingGeneration,
});
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;
case Model.Enum.AIType.DashScope:
memory.WithDashScopeDefaults(embedModel.ModelKey);
break;
}
}
private void WithTextGenerationByAIType(IKernelMemoryBuilder memory, AIModels chatModel, HttpClient chatHttpClient)
private void WithTextGenerationByAIType(IKernelMemoryBuilder memory, AIModels chatModel,
HttpClient chatHttpClient)
{
switch (chatModel.AIType)
{
@@ -113,6 +165,7 @@ namespace AntSK.Domain.Domain.Service
TextModel = chatModel.ModelName
}, null, chatHttpClient);
break;
case Model.Enum.AIType.AzureOpenAI:
memory.WithAzureOpenAITextGeneration(new AzureOpenAIConfig()
{
@@ -123,20 +176,27 @@ namespace AntSK.Domain.Domain.Service
APIType = AzureOpenAIConfig.APITypes.TextCompletion,
});
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;
case Model.Enum.AIType.DashScope:
memory.WithDashScopeTextGeneration(new Cnblogs.KernelMemory.AI.DashScope.DashScopeConfig
{
ApiKey = chatModel.ModelKey,
});
break;
}
}
private void WithMemoryDbByVectorDB(IKernelMemoryBuilder memory, IConfiguration _config)
private void WithMemoryDbByVectorDB(IKernelMemoryBuilder memory)
{
string VectorDb = _config["KernelMemory:VectorDb"].ConvertToString();
string ConnectionString = _config["KernelMemory:ConnectionString"].ConvertToString();
string TableNamePrefix = _config["KernelMemory:TableNamePrefix"].ConvertToString();
string VectorDb = KernelMemoryOption.VectorDb.ConvertToString();
string ConnectionString = KernelMemoryOption.ConnectionString.ConvertToString();
string TableNamePrefix = KernelMemoryOption.TableNamePrefix.ConvertToString();
switch (VectorDb)
{
case "Postgres":
@@ -146,14 +206,16 @@ namespace AntSK.Domain.Domain.Service
TableNamePrefix = TableNamePrefix
});
break;
case "Disk":
memory.WithSimpleFileStorage(new SimpleFileStorageConfig()
memory.WithSimpleVectorDb(new SimpleVectorDbConfig()
{
StorageType = FileSystemTypes.Disk
StorageType = FileSystemTypes.Disk,
});
break;
case "Memory":
memory.WithSimpleFileStorage(new SimpleFileStorageConfig()
memory.WithSimpleVectorDb(new SimpleVectorDbConfig()
{
StorageType = FileSystemTypes.Volatile
});
@@ -161,36 +223,103 @@ namespace AntSK.Domain.Domain.Service
}
}
public async Task<List<KMFile>> GetDocumentByFileID(string kmsid, string fileid)
public async Task<List<KMFile>> GetDocumentByFileID(string kmsId, string fileId)
{
var _memory = GetMemoryByKMS(kmsid);
var memories = await _memory.ListIndexesAsync();
var memoryDbs = _memory.Orchestrator.GetMemoryDbs();
List<KMFile> docTextList = new List<KMFile>();
var memory = GetMemoryByKMS(kmsId);
var memories = await memory.ListIndexesAsync();
var memoryDbs = memory.Orchestrator.GetMemoryDbs();
var docTextList = new List<KMFile>();
foreach (var memoryIndex in memories)
{
foreach (var memoryDb in memoryDbs)
{
var items = await memoryDb.GetListAsync(memoryIndex.Name, new List<MemoryFilter>() { new MemoryFilter().ByDocument(fileid) }, 100, true).ToListAsync();
foreach (var item in items)
var items = await memoryDb.GetListAsync(memoryIndex.Name, new List<MemoryFilter>() { new MemoryFilter().ByDocument(fileId) }, 100, true).ToListAsync();
docTextList.AddRange(items.Select(item => new KMFile()
{
KMFile file = new KMFile()
{
Text = item.Payload.FirstOrDefault(p => p.Key == "text").Value.ConvertToString(),
Url = item.Payload.FirstOrDefault(p => p.Key == "url").Value.ConvertToString(),
LastUpdate = item.Payload.FirstOrDefault(p => p.Key == "last_update").Value.ConvertToString(),
Schema = item.Payload.FirstOrDefault(p => p.Key == "schema").Value.ConvertToString(),
File = item.Payload.FirstOrDefault(p => p.Key == "file").Value.ConvertToString(),
};
docTextList.Add(file);
}
DocumentId = item.GetDocumentId(),
Text = item.GetPartitionText(),
Url = item.GetWebPageUrl(),
LastUpdate = item.GetLastUpdate().LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"),
File = item.GetFileName()
}));
}
}
return docTextList;
}
public async Task<List<RelevantSource>> GetRelevantSourceList(string kmsIdListStr, string msg)
{
var result = new List<RelevantSource>();
if (string.IsNullOrWhiteSpace(kmsIdListStr))
return result;
var kmsIdList = kmsIdListStr.Split(",");
if (!kmsIdList.Any()) return result;
var memory = GetMemoryByKMS(kmsIdList.FirstOrDefault()!);
var filters = kmsIdList.Select(kmsId => new MemoryFilter().ByTag(KmsConstantcs.KmsIdTag, kmsId)).ToList();
var searchResult = await memory.SearchAsync(msg, index: KmsConstantcs.KmsIndex, filters: filters);
if (!searchResult.NoResult)
{
foreach (var item in searchResult.Results)
{
result.AddRange(item.Partitions.Select(part => new RelevantSource()
{
SourceName = item.SourceName,
Text = Markdown.ToHtml(part.Text),
Relevance = part.Relevance
}));
}
}
return result;
}
public bool BeforeUpload(UploadFileItem file)
{
List<string> types = new List<string>() {
"text/plain",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/pdf",
"application/json",
"text/x-markdown",
"text/markdown"
};
string[] exceptExts = [".md", ".pdf"];
var validTypes = types.Contains(file.Type) || exceptExts.Contains(file.Ext);
if (!validTypes && file.Ext != ".md")
{
_message.Error("文件格式错误,请重新选择!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return validTypes && IsLt500K;
}
public void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
{
//文件列表
_fileList.Add(new UploadFileItem()
{
FileName = fileinfo.File.FileName,
Url = fileinfo.File.Url = fileinfo.File.Response
});
}
}
}
}
}

View File

@@ -2,7 +2,6 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using LLama;
@@ -15,6 +14,9 @@ using RestSharp;
using System;
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
using AntSK.LLM.Mock;
using AntSK.Domain.Domain.Model.Enum;
using System.Reflection;
using DocumentFormat.OpenXml.Drawing;
namespace AntSK.Domain.Domain.Service
{
@@ -24,20 +26,21 @@ namespace AntSK.Domain.Domain.Service
private readonly IApis_Repositories _apis_Repositories;
private readonly IAIModels_Repositories _aIModels_Repositories;
private readonly FunctionService _functionService;
private readonly IServiceProvider _serviceProvider;
private Kernel _kernel;
public KernelService(
IApis_Repositories apis_Repositories,
IAIModels_Repositories aIModels_Repositories,
FunctionService functionService)
FunctionService functionService,
IServiceProvider serviceProvider)
{
_apis_Repositories = apis_Repositories;
_aIModels_Repositories = aIModels_Repositories;
_functionService = functionService;
_serviceProvider = serviceProvider;
}
private Kernel _kernel;
/// <summary>
/// 获取kernel实例依赖注入不好按每个用户去Import不同的插件所以每次new一个新的kernel
/// </summary>
@@ -94,6 +97,11 @@ namespace AntSK.Domain.Domain.Service
var options = new SparkDeskOptions { AppId = chatModel.EndPoint, ApiSecret = chatModel.ModelKey, ApiKey = chatModel.ModelName, ModelVersion = Sdcb.SparkDesk.ModelVersion.V3_5 };
builder.Services.AddKeyedSingleton<ITextGenerationService>("spark-desk", new SparkDeskTextCompletion(options, app.Id));
break;
case Model.Enum.AIType.DashScope:
builder.Services.AddDashScopeChatCompletion(chatModel.ModelKey, chatModel.ModelName);
break;
case Model.Enum.AIType.Mock:
builder.Services.AddKeyedSingleton<ITextGenerationService>("mock", new MockTextCompletion());
break;
@@ -112,9 +120,23 @@ namespace AntSK.Domain.Domain.Service
{
return;
}
List<KernelFunction> apiFunctions = new List<KernelFunction>();
List<KernelFunction> functions = new List<KernelFunction>();
//API插件
ImportApiFunction(app, functions);
//本地函数插件
ImportNativeFunction(app, functions);
_kernel.ImportPluginFromFunctions("AntSkFunctions", functions);
}
/// <summary>
/// 导入API插件
/// </summary>
/// <param name="app"></param>
/// <param name="functions"></param>
private void ImportApiFunction(Apps app, List<KernelFunction> functions)
{
if (!string.IsNullOrWhiteSpace(app.ApiFunctionList))
{
//开启自动插件调用
@@ -123,17 +145,27 @@ namespace AntSK.Domain.Domain.Service
foreach (var api in apiList)
{
var returnType = new KernelReturnParameterMetadata() { Description = api.OutputPrompt };
switch (api.Method)
{
case HttpMethodType.Get:
apiFunctions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
var getParametes = new List<KernelParameterMetadata>() {
new KernelParameterMetadata("jsonbody"){
Name="json参数字符串",
ParameterType=typeof(string),
Description=$"需要根据背景文档:{Environment.NewLine}{api.InputPrompt} {Environment.NewLine}提取出对应的json格式字符串参考如下格式:{Environment.NewLine}{api.Query}"
}
};
functions.Add(_kernel.CreateFunctionFromMethod((string jsonbody) =>
{
try
{
Console.WriteLine(msg);
//将json 转换为query参数
var queryString = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonbody);
RestClient client = new RestClient();
RestRequest request = new RestRequest(api.Url, Method.Get);
foreach (var header in api.Header.Split("\n"))
foreach (var header in api.Header.ConvertToString().Split("\n"))
{
var headerArray = header.Split(":");
if (headerArray.Length == 2)
@@ -142,13 +174,9 @@ namespace AntSK.Domain.Domain.Service
}
}
//这里应该还要处理一次参数提取,等后面再迭代
foreach (var query in api.Query.Split("\n"))
foreach (var q in queryString)
{
var queryArray = query.Split("=");
if (queryArray.Length == 2)
{
request.AddQueryParameter(queryArray[0], queryArray[1]);
}
request.AddQueryParameter(q.Key, q.Value);
}
var result = client.Execute(request);
return result.Content;
@@ -157,18 +185,25 @@ namespace AntSK.Domain.Domain.Service
{
return "调用失败:" + ex.Message;
}
}, api.Name, $"{api.Describe}"));
}, api.Name, api.Describe, getParametes, returnType));
break;
case HttpMethodType.Post:
apiFunctions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
//处理json body
var postParametes = new List<KernelParameterMetadata>() {
new KernelParameterMetadata("jsonbody"){
Name="json参数字符串",
ParameterType=typeof(string),
Description=$"需要根据背景文档:{Environment.NewLine}{api.InputPrompt} {Environment.NewLine}提取出对应的json格式字符串参考如下格式:{Environment.NewLine}{api.JsonBody}"
}
};
functions.Add(_kernel.CreateFunctionFromMethod((string jsonBody) =>
{
try
{
Console.WriteLine(msg);
Console.WriteLine(jsonBody);
RestClient client = new RestClient();
RestRequest request = new RestRequest(api.Url, Method.Post);
foreach (var header in api.Header.Split("\n"))
foreach (var header in api.Header.ConvertToString().Split("\n"))
{
var headerArray = header.Split(":");
if (headerArray.Length == 2)
@@ -177,7 +212,7 @@ namespace AntSK.Domain.Domain.Service
}
}
//这里应该还要处理一次参数提取,等后面再迭代
request.AddJsonBody(api.JsonBody);
request.AddJsonBody(jsonBody.ConvertToString());
var result = client.Execute(request);
return result.Content;
}
@@ -185,18 +220,28 @@ namespace AntSK.Domain.Domain.Service
{
return "调用失败:" + ex.Message;
}
}, api.Name, $"{api.Describe}"));
}, api.Name, api.Describe, postParametes, returnType));
break;
}
}
}
//本地函数插件
}
/// <summary>
/// 导入原生插件
/// </summary>
/// <param name="app"></param>
/// <param name="functions"></param>
private void ImportNativeFunction(Apps app, List<KernelFunction> functions)
{
if (!string.IsNullOrWhiteSpace(app.NativeFunctionList))//需要添加判断应用是否开启了本地函数插件
{
var nativeIdList = app.NativeFunctionList.Split(",");
_functionService.SearchMarkedMethods();
using var scope = _serviceProvider.CreateScope();
foreach (var func in _functionService.Functions)
{
if (nativeIdList.Contains(func.Key))
@@ -204,11 +249,11 @@ namespace AntSK.Domain.Domain.Service
var methodInfo = _functionService.MethodInfos[func.Key];
var parameters = methodInfo.Parameters.Select(x => new KernelParameterMetadata(x.ParameterName) { ParameterType = x.ParameterType, Description = x.Description });
var returnType = new KernelReturnParameterMetadata() { ParameterType = methodInfo.ReturnType.ParameterType, Description = methodInfo.ReturnType.Description };
apiFunctions.Add(_kernel.CreateFunctionFromMethod((object[] args) => func.Value(args), func.Key, methodInfo.Description, parameters, returnType));
var target = ActivatorUtilities.CreateInstance(scope.ServiceProvider, func.Value.DeclaringType);
functions.Add(_kernel.CreateFunctionFromMethod(func.Value, target, func.Key, methodInfo.Description, parameters, returnType));
}
}
}
_kernel.ImportPluginFromFunctions("AntSkFunctions", apiFunctions);
}
/// <summary>
@@ -218,8 +263,8 @@ namespace AntSK.Domain.Domain.Service
private void RegisterPluginsWithKernel(Kernel kernel)
{
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
kernel.ImportPluginFromPromptDirectory(Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
//kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
kernel.ImportPluginFromPromptDirectory(System.IO.Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
}
/// <summary>
@@ -234,7 +279,7 @@ namespace AntSK.Domain.Domain.Service
KernelFunction sunFun = _kernel.Plugins.GetFunction("ConversationSummaryPlugin", "SummarizeConversation");
var summary = await _kernel.InvokeAsync(sunFun, new() { ["input"] = $"内容是:{history.ToString()} {Environment.NewLine} 请注意用中文总结" });
string his = summary.GetValue<string>();
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}"; ;
var msg = $"history{Environment.NewLine}{history.ToString()}{Environment.NewLine} user{questions}{Environment.NewLine}"; ;
return msg;
}
}

View File

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

View File

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

View File

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

View File

@@ -1,23 +0,0 @@
namespace AntSK.Domain.Model.Enum
{
/// <summary>
/// AI类型
/// </summary>
public enum AIType
{
OpenAI = 1,
AzureOpenAI = 2,
LLamaSharp=3,
SparkDesk=4,
Mock=5,
}
/// <summary>
/// 模型类型
/// </summary>
public enum AIModelType
{
Chat = 1,
Embedding = 2,
}
}

View File

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

View File

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

View File

@@ -20,6 +20,7 @@ namespace AntSK.Domain.Repositories
/// </summary>
[Required]
public string Describe { get; set; }
/// <summary>
/// 图标
/// </summary>
@@ -38,6 +39,11 @@ namespace AntSK.Domain.Repositories
[Required]
public string? ChatModelID { get; set; }
/// <summary>
/// Embedding 模型Id
/// </summary>
public string? EmbeddingModelID { get; set; }
/// <summary>
/// 温度
/// </summary>
@@ -61,14 +67,14 @@ namespace AntSK.Domain.Repositories
[SugarColumn(ColumnDataType = "varchar(1000)")]
public string? NativeFunctionList { get; set; }
/// <summary>
/// 知识库ID列表
/// </summary>
public string? KmsIdList { get; set; }
/// <summary>
/// API调用秘钥
/// </summary>
public string? SecretKey { get; set; }
}
}
}

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
namespace AntSK.Domain.Utils
using System.Web;
namespace AntSK.Domain.Utils
{
public static class ConvertUtils
{
@@ -231,5 +233,22 @@
{
return $"{Environment.NewLine}```json{Environment.NewLine}{s}{Environment.NewLine}```{Environment.NewLine}";
}
/// <summary>
/// json参数转化querystring参数
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
public static string ToQueryString(this Dictionary<string, string> parameters)
{
var nameValueCollection = HttpUtility.ParseQueryString(string.Empty);
foreach (var param in parameters)
{
nameValueCollection[param.Key] = param.Value;
}
return nameValueCollection.ToString();
}
}
}

View File

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

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

@@ -0,0 +1,22 @@
using System.ComponentModel;
namespace AntSK.Test
{
/// <summary>
/// 测试插件导入
/// </summary>
public class TestFunctionImport
{
//导入的Function Call插件请安此格式书写函数描述需要以AntSK:开头,返回值描述需要以return:Description,参数描述需要使用Description
[Description("AntSK:获取产品详情")]
[return: Description("返回信息详情")]
public string GetInfo([Description("产品名称")] string Name)
{
return $"""
我的名字是{Name},
我的作者是许泽宇
我是一个AI 知识库/智能体项目
""";
}
}
}

View File

@@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.BackgroundTask", "Mid
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.LLM", "AntSk.LLM\AntSK.LLM.csproj", "{19529BFA-152F-4A8C-8254-F2D4896AB739}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntSK.Test", "AntSK.Test\AntSK.Test.csproj", "{6AD71410-127F-4C83-95A8-F699C39B44FF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -42,6 +44,10 @@ Global
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19529BFA-152F-4A8C-8254-F2D4896AB739}.Release|Any CPU.Build.0 = Release|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AD71410-127F-4C83-95A8-F699C39B44FF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
<PackageReference Include="Downloader" Version="3.0.6" />
</ItemGroup>
<ItemGroup>
@@ -22,6 +23,9 @@
</ItemGroup>
<ItemGroup>
<None Update="plugins\KMSPlugin\Ask1\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="plugins\KMSPlugin\Ask\skprompt.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

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

View File

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

View File

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

View File

@@ -1,34 +1,37 @@
using AntSK.BackgroundTask;
using AntSK.Domain.Common.Map;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Map;
using AntSK.Domain.Model;
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Repositories;
using Microsoft.AspNetCore.Mvc;
namespace AntSK.Controllers
{
/// <summary>
///
/// KMSController
/// </summary>
/// <param name="_taskBroker"></param>
[Route("api/[controller]/[action]")]
[ApiController]
public class KMSController : ControllerBase
{
private readonly IKmsDetails_Repositories _kmsDetails_Repositories;
private readonly IKMService _iKMService;
private readonly IKmsDetails_Repositories _kmsDetailsRepositories;
private readonly BackgroundTaskBroker<ImportKMSTaskReq> _taskBroker;
public KMSController(
IKmsDetails_Repositories kmsDetails_Repositories,
IKMService iKMService,
IKmsDetails_Repositories kmsDetailsRepositories,
BackgroundTaskBroker<ImportKMSTaskReq> taskBroker
)
)
{
_kmsDetails_Repositories = kmsDetails_Repositories;
_iKMService = iKMService;
_kmsDetailsRepositories = kmsDetailsRepositories;
_taskBroker = taskBroker;
}
/// <summary>
/// 导入任务
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> ImportKMSTask(ImportKMSTaskDTO model)
{
@@ -43,11 +46,11 @@ namespace AntSK.Controllers
Type = model.ImportType.ToString().ToLower()
};
_kmsDetails_Repositories.Insert(detail);
await _kmsDetailsRepositories.InsertAsync(detail);
req.KmsDetail = detail;
_taskBroker.QueueWorkItem(req);
Console.WriteLine("api/kms/ImportKMSTask 结束");
return Ok();
}
}
}
}

View File

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

View File

@@ -1,11 +1,10 @@
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Domain.Utils;
using AntSK.Services.OpenApi;
using Microsoft.AspNetCore.Mvc;
namespace AntSK.Controllers
{
/// <summary>
/// 对外接口
/// </summary>
@@ -18,10 +17,10 @@ namespace AntSK.Controllers
/// <returns></returns>
[HttpPost]
[Route("api/v1/chat/completions")]
public async Task chat(OpenAIModel model)
public async Task Chat(OpenAIModel model)
{
string sk = HttpContext.Request.Headers["Authorization"].ConvertToString();
await _openApiService.Chat(model, sk, HttpContext);
}
}
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace AntSK.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class TestFunctionCallController : ControllerBase
{
[HttpPost]
public IActionResult GetInfo(InfoDto dto)
{
Console.Write(JsonConvert.SerializeObject(dto));
return Ok($"你的姓名是:{dto.name},年龄是:{dto.age},性别是男,爱好编程");
}
}
public class InfoDto
{
public string name { get; set; }
public string age { get; set; }
}
}

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
@namespace AntSK.Pages.AppPage
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model.Enum
@using AntSK.Domain.Domain.Model.Enum
@page "/App/Add"
@page "/App/Add/{AppId}"
@using AntSK.Services.Auth
@@ -38,6 +38,14 @@
</Select>
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
</FormItem>
<FormItem Label="Embedding模型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Select DataSource="@_embedignList"
@bind-Value="@context.EmbeddingModelID"
ValueProperty="c=>c.Id"
LabelProperty="c=>'【'+c.AIType.ToString()+'】'+c.ModelDescription">
</Select>
<Button Type="@ButtonType.Link" OnClick="NavigateModelList">去创建</Button>
</FormItem>
@if (@context.Type == AppType.chat.ToString())
{

View File

@@ -1,7 +1,8 @@
using AntDesign;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.SemanticKernel;
@@ -26,7 +27,7 @@ namespace AntSK.Pages.AppPage
protected MessageService? Message { get; set; }
[Inject]
protected IAIModels_Repositories _aimodels_Repositories { get; set; }
[Inject]
[Inject]
protected FunctionService _functionService { get; set; }
private Apps _appModel = new Apps();
@@ -35,30 +36,32 @@ namespace AntSK.Pages.AppPage
private List<Kmss> _kmsList = new List<Kmss>();
IEnumerable<string> apiIds=[];
IEnumerable<string> apiIds = [];
private List<Apis> _apiList = new List<Apis>();
IEnumerable<string> funIds=[];
IEnumerable<string> funIds = [];
public Dictionary<string, string> _funList = new Dictionary<string, string>();
private List<AIModels> _chatList { get; set; }
private List<AIModels> _chatList;
private List<AIModels> _embedignList;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_kmsList = _kmss_Repositories.GetList();
_apiList = _apis_Repositories.GetList();
var models=_aimodels_Repositories.GetList();
_chatList = models.Where(p => p.AIModelType == AIModelType.Chat).ToList();
_embedignList = models.Where(p => p.AIModelType == AIModelType.Embedding).ToList();
_chatList = _aimodels_Repositories.GetList(p => p.AIModelType == AIModelType.Chat);
_functionService.SearchMarkedMethods();
foreach (var func in _functionService.Functions)
{
var methodInfo = _functionService.MethodInfos[func.Key];
var methodInfo = _functionService.MethodInfos[func.Key];
_funList.Add(func.Key, methodInfo.Description);
}
if (!string.IsNullOrEmpty(AppId))
{
@@ -66,7 +69,7 @@ namespace AntSK.Pages.AppPage
_appModel = _apps_Repositories.GetFirst(p => p.Id == AppId);
kmsIds = _appModel.KmsIdList?.Split(",");
apiIds = _appModel.ApiFunctionList?.Split(",");
funIds= _appModel.NativeFunctionList?.Split(",");
funIds = _appModel.NativeFunctionList?.Split(",");
}
@@ -85,9 +88,15 @@ namespace AntSK.Pages.AppPage
_appModel.KmsIdList = string.Join(",", kmsIds);
}
_appModel.ApiFunctionList = string.Join(",", apiIds);
if (apiIds.IsNotNull())
{
_appModel.ApiFunctionList = string.Join(",", apiIds);
}
if (funIds.IsNotNull())
{
_appModel.NativeFunctionList = string.Join(",", funIds);
_appModel.NativeFunctionList = string.Join(",", funIds);
}
if (string.IsNullOrEmpty(AppId))
{

View File

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

View File

@@ -1,5 +1,5 @@
using AntDesign;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Domain.Repositories;
using AntSK.Models;
using Microsoft.AspNetCore.Components;
@@ -58,7 +58,7 @@ namespace AntSK.Pages.AppPage
private void GetDesc()
{
_desc = @$"为了方便其他应用对接接口符合openai规范省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "" } } }, Formatting.Indented)}";
_desc = @$"为了方便其他应用对接接口符合openai规范省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization:Bearer ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "" } } }, Formatting.Indented)}";
}
private void GetScript()

View File

@@ -10,7 +10,7 @@
<GridRow Gutter="(16, 16)">
<GridCol Span="12">
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
<Card Style="height:700px;overflow: auto;">
<Card Style="height:75vh;overflow: auto;">
<TitleTemplate>
<Icon Type="setting" /> 选择应用
<Select DataSource="@_list"
@@ -20,9 +20,10 @@
LabelProperty="c=>c.Name"
Style="width:200px">
</Select>
<a href="@( NavigationManager.BaseUri + "openchat/" + AppId)" target="_blank">分享使用</a>
</TitleTemplate>
<Body>
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
<div id="scrollDiv" style="height: calc(75vh - 190px); overflow-y: auto; overflow-x: hidden;">
<GridRow Gutter="(8, 8)" Style="margin:0">
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
@if (item.IsSend)
@@ -31,7 +32,17 @@
<GridCol Span="23">
<div class="chat-bubble sent">
<Popover Title="@item.CreateTime.ToString()">
@(item.Context)
<Unbound>
<Flex Vertical RefBack="context">
@if (item.FileName != null)
{
<p class="message-file">
<Upload DefaultFileList="[new(){ FileName= item.FileName }]" />
</p>
}
<p>@(item.Context)</p>
</Flex>
</Unbound>
</Popover>
</div>
<Icon Style="float:right;margin-top:10px;" Type="copy" Theme="outline" OnClick="async () =>await OnCopyAsync(item)" />
@@ -57,19 +68,33 @@
</Virtualize>
</GridRow>
</div>
<div style="height: 10px;"></div>
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
<Suffix>
<Button Icon="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>
<Flex Vertical>
@if (fileList.Count > 0)
{
<Upload DefaultFileList="fileList" OnRemove="HandleFileRemove" />
}
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding"></AntDesign.Input>
</Flex>
<Flex Justify="end">
<Upload Action="@("api/File/UploadFile")"
Name="file"
Accept="*/*"
ShowUploadList="false"
BeforeUpload="_kMService.BeforeUpload"
OnSingleCompleted="OnSingleCompleted">
<Button Icon="@IconType.Outline.Upload" Type="@(ButtonType.Link)" />
</Upload>
<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>
</Flex>
</Body>
</Card>
</Spin>
</GridCol>
<GridCol Span="12">
<Card Style="height: 700px;overflow: auto;">
<Card Style="height: 75vh;overflow: auto;">
<TitleTemplate>
<Icon Type="search" /> 调试结果
</TitleTemplate>
@@ -78,62 +103,61 @@
</Extra>
<Body>
<AntList Bordered DataSource="@_relevantSources" Style="padding:10px;">
<ChildContent Context="item">
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<Body>
@((MarkupString)(@item.Text))
</Body>
</ChildContent>
</AntList>
</Body>
</Card>
</GridCol>
</GridRow>
<ChildContent Context="item">
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
<Body>
@((MarkupString)(@item.Text))
</Body>
</ChildContent>
</AntList>
</Body>
</Card>
</GridCol>
</GridRow>
<style>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 10px;
justify-content: center;
align-items: flex-start;
height: 100vh;
}
body {
font-family: Arial, sans-serif;
margin: 0;
justify-content: center;
align-items: flex-start;
height: 100vh;
}
.chat-container {
width: 350px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #fff;
padding-bottom: 15px;
}
.chat-container {
width: 350px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #fff;
padding-bottom: 15px;
}
.chat-bubble {
padding: 10px;
margin: 10px;
margin-bottom: 0;
border-radius: 5px;
max-width: 70%;
position: relative;
}
.chat-bubble {
padding: 10px;
margin: 10px;
margin-bottom: 0;
border-radius: 5px;
max-width: 70%;
position: relative;
}
.received {
background-color: #f0f0f0;
align-self: flex-start;
float: left;
}
.received {
background-color: #f0f0f0;
align-self: flex-start;
float: left;
}
.sent {
background-color: #daf8cb;
align-self: flex-end;
float: right;
position: relative;
}
</style>
@code {
.sent {
background-color: #daf8cb;
align-self: flex-end;
float: right;
position: relative;
}
</style>
@code {
}

View File

@@ -1,48 +1,32 @@
using AntDesign;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Microsoft.KernelMemory;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using RestSharp;
using System.Text;
using AntSK.Domain.Utils;
using Markdig;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Model.Dto;
namespace AntSK.Pages.ChatPage
{
public partial class Chat
{
[Parameter]
public string AppId { get; set; }
[Inject]
protected MessageService? Message { get; set; }
[Inject]
protected IApps_Repositories _apps_Repositories { get; set; }
[Inject]
protected IApis_Repositories _apis_Repositories { get; set; }
[Inject]
protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject] IJSRuntime _JSRuntime { get; set; }
[Parameter] public string AppId { get; set; }
[Inject] protected MessageService? Message { get; set; }
[Inject] protected IApps_Repositories _apps_Repositories { get; set; }
[Inject] protected IApis_Repositories _apis_Repositories { get; set; }
[Inject] protected IKmss_Repositories _kmss_Repositories { get; set; }
[Inject] protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
[Inject] private IJSRuntime _JSRuntime { get; set; }
[Inject]
protected IKernelService _kernelService { get; set; }
[Inject]
protected IKMService _kMService { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
[Inject]
IChatService _chatService { get; set; }
[Inject] protected IKernelService _kernelService { get; set; }
[Inject] protected IKMService _kMService { get; set; }
[Inject] private IConfirmService _confirmService { get; set; }
[Inject] private IChatService _chatService { get; set; }
[Inject]
private ILogger<Chat> Logger { get; set; }
[Inject] private ILogger<Chat> Logger { get; set; }
protected bool _loading = false;
protected List<MessageInfo> MessageList = [];
@@ -50,14 +34,20 @@ namespace AntSK.Pages.ChatPage
protected string _json = "";
protected bool Sendding = false;
List<RelevantSource> _relevantSources = new List<RelevantSource>();
private List<RelevantSource> _relevantSources = new List<RelevantSource>();
protected List<Apps> _list = new List<Apps>();
private List<UploadFileItem> fileList = [];
private Upload _uploadRef;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_list = _apps_Repositories.GetList();
}
protected async Task OnSendAsync()
{
try
@@ -74,33 +64,40 @@ namespace AntSK.Pages.ChatPage
return;
}
var filePath = fileList.FirstOrDefault()?.Url;
var fileName = fileList.FirstOrDefault()?.FileName;
MessageList.Add(new MessageInfo()
{
ID = Guid.NewGuid().ToString(),
Context = _messageInput,
CreateTime = DateTime.Now,
IsSend = true
IsSend = true,
FilePath = filePath,
FileName = fileName
});
var prompt = _messageInput;
_messageInput = "";
fileList.Clear();
Sendding = true;
await SendAsync(_messageInput);
_messageInput = "";
await SendAsync(prompt, filePath);
Sendding = false;
}
catch (System.Exception ex)
{
Sendding = false;
Logger.LogError( ex,"对话异常");
_ = Message.Error("异常:"+ex.Message, 2);
Logger.LogError(ex, "对话异常");
_ = Message.Error("异常:" + ex.Message, 2);
}
}
protected async Task OnCopyAsync(MessageInfo item)
{
await Task.Run(() =>
{
_messageInput = item.Context;
});
await Task.Run(() => { _messageInput = item.Context; });
}
protected async Task OnClearAsync()
@@ -121,7 +118,8 @@ namespace AntSK.Pages.ChatPage
_ = Message.Info("没有会话记录");
}
}
protected async Task<bool> SendAsync(string questions)
protected async Task<bool> SendAsync(string questions, string? filePath)
{
string msg = "";
//处理多轮会话
@@ -133,13 +131,14 @@ namespace AntSK.Pages.ChatPage
switch (app.Type)
{
case "chat":
case "chat" when filePath == null:
//普通会话
await SendChat(questions, msg, app);
break;
case "kms":
default:
//知识库问答
await SendKms(questions, msg, app);
await SendKms(questions, msg, filePath, app);
break;
}
@@ -151,14 +150,13 @@ namespace AntSK.Pages.ChatPage
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="filePath"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task SendKms(string questions, string msg, Apps app)
private async Task SendKms(string questions, string msg, string filePath, Apps app)
{
MessageInfo info = null;
var chatResult = _chatService.SendKmsByAppAsync(app, questions, msg, _relevantSources);
var chatResult = _chatService.SendKmsByAppAsync(app, questions, msg, filePath, _relevantSources);
await foreach (var content in chatResult)
{
if (info == null)
@@ -176,15 +174,14 @@ namespace AntSK.Pages.ChatPage
info.HtmlAnswers += content.ConvertToString();
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
await MarkDown(info);
}
/// <summary>
/// 发送普通对话
/// </summary>
@@ -194,7 +191,7 @@ namespace AntSK.Pages.ChatPage
/// <returns></returns>
private async Task SendChat(string questions, string history, Apps app)
{
MessageInfo info =null;
MessageInfo info = null;
var chatResult = _chatService.SendChatByAppAsync(app, questions, history);
await foreach (var content in chatResult)
{
@@ -213,25 +210,27 @@ namespace AntSK.Pages.ChatPage
info.HtmlAnswers += content.ConvertToString();
await Task.Delay(50);
}
await InvokeAsync(StateHasChanged);
}
//全部处理完后再处理一次Markdown
await MarkDown(info);
}
private async Task MarkDown(MessageInfo info)
{
if (info.IsNotNull())
{
// info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
info!.HtmlAnswers = Markdown.ToHtml(info.HtmlAnswers);
}
await InvokeAsync(StateHasChanged);
await _JSRuntime.InvokeVoidAsync("Prism.highlightAll");
await _JSRuntime.ScrollToBottomAsync("scrollDiv");
}
/// <summary>
/// 历史会话的会话总结
/// </summary>
@@ -254,6 +253,7 @@ namespace AntSK.Pages.ChatPage
history.Append($"assistant:{item.Context}{Environment.NewLine}");
}
}
if (MessageList.Count > 10)
{
//历史会话大于10条进行总结
@@ -262,7 +262,8 @@ namespace AntSK.Pages.ChatPage
}
else
{
var msg = $"history{history.ToString()}{Environment.NewLine}";
var msg =
$"history{Environment.NewLine}{history.ToString()}{Environment.NewLine}{Environment.NewLine}";
return msg;
}
}
@@ -272,7 +273,23 @@ namespace AntSK.Pages.ChatPage
}
}
}
private void OnSingleCompleted(UploadInfo fileInfo)
{
fileList.Add(new()
{
FileName = fileInfo.File.FileName,
Url = fileInfo.File.Url = fileInfo.File.Response,
Ext = fileInfo.File.Ext,
State = UploadState.Success,
});
_kMService.OnSingleCompleted(fileInfo);
}
}
private async Task<bool> HandleFileRemove(UploadFileItem file)
{
fileList.RemoveAll(x => x.FileName == file.FileName);
await Task.Yield();
return true;
}
}
}

View File

@@ -1,6 +1,5 @@
using AntDesign;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.AspNetCore.Components;
@@ -12,6 +11,7 @@ using System.Text;
using AntSK.Domain.Utils;
using Microsoft.JSInterop;
using Markdig;
using AntSK.Domain.Domain.Model;
namespace AntSK.Pages.ChatPage
{
@@ -152,7 +152,7 @@ namespace AntSK.Pages.ChatPage
private async Task SendKms(string questions, string msg, Apps app)
{
MessageInfo info = null;
var chatResult=_chatService.SendKmsByAppAsync(app, questions, msg);
var chatResult=_chatService.SendKmsByAppAsync(app, questions, "" ,msg);
await foreach (var content in chatResult)
{
if (info == null)

View File

@@ -1,4 +1,4 @@
using AntSK.Domain.Model.Enum;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.AspNetCore.Components;

View File

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

View File

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

View File

@@ -141,8 +141,8 @@
Drag
Multiple
Accept="*/*"
BeforeUpload="BeforeUpload"
OnSingleCompleted="OnSingleCompleted">
BeforeUpload="iKMService.BeforeUpload"
OnSingleCompleted="iKMService.OnSingleCompleted">
<p class="ant-upload-drag-icon">
<Icon Type="inbox" />
</p>

View File

@@ -1,7 +1,7 @@
using AntDesign;
using AntSK.BackgroundTask;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Model;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Repositories;
using AntSK.Models;
using DocumentFormat.OpenXml.Drawing.Diagrams;
@@ -21,16 +21,16 @@ namespace AntSK.Pages.KmsPage
private readonly KmsDetails _model = new KmsDetails();
bool _urlVisible = false;
bool _urlConfirmLoading = false;
private bool _urlVisible = false;
private bool _urlConfirmLoading = false;
bool _fileVisible = false;
bool _fileConfirmLoading = false;
private bool _fileVisible = false;
private bool _fileConfirmLoading = false;
bool _textVisible = false;
bool _textConfirmLoading = false;
private bool _textVisible = false;
private bool _textConfirmLoading = false;
List<FileInfoModel> fileList = new List<FileInfoModel>();
private List<FileInfoModel> fileList = new List<FileInfoModel>();
private Form<UrlModel> _urlForm;
private UrlModel urlModel = new UrlModel();
@@ -50,6 +50,7 @@ namespace AntSK.Pages.KmsPage
[Inject]
protected IConfirmService _confirmService { get; set; }
[Inject]
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
@@ -57,16 +58,19 @@ namespace AntSK.Pages.KmsPage
protected IKmss_Repositories _kmss_Repositories { get; set; }
private MemoryServerless _memory { get; set; }
[Inject]
protected IKMService iKMService { get; set; }
[Inject]
protected MessageService? _message { get; set; }
[Inject]
protected BackgroundTaskBroker<ImportKMSTaskReq> _taskBroker { get; set; }
[Inject]
protected IHttpService _httpService { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
@@ -81,6 +85,7 @@ namespace AntSK.Pages.KmsPage
{
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
}
/// <summary>
/// 根据文档ID获取文档
/// </summary>
@@ -88,11 +93,13 @@ namespace AntSK.Pages.KmsPage
/// <returns></returns>
#region Url
public class UrlModel
{
[Required]
public string Url { get; set; }
}
private async Task UrlHandleOk(MouseEventArgs e)
{
try
@@ -106,22 +113,25 @@ namespace AntSK.Pages.KmsPage
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
_urlVisible = false;
urlModel.Url = "";
_ = _message.Info("加入队列,进入后台处理中!", 2);
_ = _message.Info("加入队列,进入后台处理中!", 2);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void UrlHandleCancel(MouseEventArgs e)
{
_urlVisible = false;
}
private void UrlShowModal()
{
_urlVisible = true;
}
#endregion
#endregion Url
#region Text
@@ -130,6 +140,7 @@ namespace AntSK.Pages.KmsPage
[Required]
public string Text { get; set; }
}
private async Task TextHandleOk(MouseEventArgs e)
{
try
@@ -144,26 +155,27 @@ namespace AntSK.Pages.KmsPage
_textVisible = false;
textModel.Text = "";
_ = _message.Info("加入队列,进入后台处理中!", 2);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void TextHandleCancel(MouseEventArgs e)
{
_textVisible = false;
}
private void TextShowModal()
{
_textVisible = true;
}
#endregion
#endregion Text
#region File
private async Task FileHandleOk(MouseEventArgs e)
{
try
@@ -177,7 +189,7 @@ namespace AntSK.Pages.KmsPage
FilePath = item.FilePath,
FileName = item.FileName
});
}
}
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
//上传文档
_fileVisible = false;
@@ -189,56 +201,17 @@ namespace AntSK.Pages.KmsPage
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void FileHandleCancel(MouseEventArgs e)
{
_fileVisible = false;
}
private void FileShowModal()
{
_fileVisible = true;
}
bool BeforeUpload(UploadFileItem file)
{
List<string> types = new List<string>() {
"text/plain",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/pdf",
"application/json",
"text/x-markdown",
"text/markdown"
};
var IsType = types.Contains(file.Type);
if (!IsType && file.Ext != ".md")
{
_message.Error("文件格式错误,请重新选择!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return IsType && IsLt500K;
}
private void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
{
//文件列表
fileList.Add(new FileInfoModel()
{
FileName = fileinfo.File.FileName,
FilePath = fileinfo.File.Url = fileinfo.File.Response
});
}
}
private void FileDetail(string fileid)
{
NavigationManager.NavigateTo($"/kms/detaillist/{KmsId}/{fileid}");
@@ -271,6 +244,6 @@ namespace AntSK.Pages.KmsPage
}
}
#endregion
#endregion File
}
}
}

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
@namespace AntSK.Pages.ApiPage
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model
@using AntSK.Domain.Domain.Model.Enum
@page "/plugins/api/add"
@page "/plugins/api/add/{ApiId}"
@using AntSK.Services.Auth
@@ -14,7 +14,7 @@
Style="margin-top: 8px;"
OnFinish="HandleSubmit">
<FormItem Label="注意" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<h1>API插件需要使用FunctionCall能力建议使用GPT4国产模型可能不太行</h1>
<h3>API插件需要使用Function Call能力建议使用GPT4国产模型可能不太行</h3>
</FormItem>
<FormItem Label="Api名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入Api名称" @bind-Value="@context.Name" />
@@ -35,7 +35,7 @@
@if (context.Method == HttpMethodType.Get)
{
<FormItem Label="Query" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<TextArea Placeholder="一个一行,以等号分割。例如:name=a" @bind-Value="@context.Query" MinRows="5" />
<TextArea Placeholder="把query构造成json格式便于大模型识别" @bind-Value="@context.Query" MinRows="5" />
</FormItem>
}
else if (context.Method == HttpMethodType.Post)

View File

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

View File

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

View File

@@ -0,0 +1,144 @@
using AntDesign;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Fun;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Repositories;
using AntSK.Models;
using DocumentFormat.OpenXml.Office2010.Excel;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.SemanticKernel;
namespace AntSK.Pages.FunPage
{
public partial class FunList
{
private FunDto[] _data = { };
[Inject]
FunctionService _functionService { get; set; }
[Inject]
IServiceProvider _serviceProvider { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
[Inject]
IFuns_Repositories _funs_Repositories { get; set; }
[Inject]
protected MessageService? _message { get; set; }
bool _fileVisible = false;
bool _fileConfirmLoading = false;
List<FileInfoModel> fileList = new List<FileInfoModel>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await InitData("");
}
private async Task InitData(string searchKey)
{
var list = new List<FunDto> { new FunDto() };
_functionService.SearchMarkedMethods();
using var scope = _serviceProvider.CreateScope();
var funList = _functionService.Functions;
if (!string.IsNullOrEmpty(searchKey))
{
funList = funList.Where(x => x.Key.Contains(searchKey)).ToDictionary(x => x.Key, x => x.Value);
}
foreach (var func in funList)
{
var methodInfo = _functionService.MethodInfos[func.Key];
list.Add(new FunDto() { Name = func.Key, Description = methodInfo.Description });
}
_data = list.ToArray();
await InvokeAsync(StateHasChanged);
}
private void NavigateToAddApp()
{
NavigationManager.NavigateTo("/plugins/fun/add");
}
private async Task Search(string searchKey)
{
await InitData(searchKey);
}
private async Task AddFun() {
_fileVisible = true;
}
private async Task ClearFun()
{
var content = "清空自定义函数将会删除全部导入函数并且需要程序重启后下次生效如不是DLL冲突等原因不建议清空是否要清空";
var title = "清空自定义函数";
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
if (result == ConfirmResult.Yes)
{
await _funs_Repositories.DeleteAsync(p=>true);
await InitData("");
}
}
private async Task FileHandleOk(MouseEventArgs e)
{
try
{
foreach (var file in fileList)
{
_funs_Repositories.Insert(new Funs() { Id = Guid.NewGuid().ToString(), Path = file.FilePath });
_functionService.FuncLoad(file.FilePath);
}
_message.Info("上传成功");
await InitData("");
_fileVisible = false;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void FileHandleCancel(MouseEventArgs e)
{
_fileVisible = false;
}
private void FileShowModal()
{
_fileVisible = true;
}
bool BeforeUpload(UploadFileItem file)
{
if (file.Ext != ".dll")
{
_message.Error("请上传dll文件!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return IsLt500K;
}
private void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
{
//文件列表
fileList.Add(new FileInfoModel()
{
FileName = fileinfo.File.FileName,
FilePath = fileinfo.File.Url = fileinfo.File.Response
});
}
}
}
}

View File

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

View File

@@ -1,9 +1,10 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Models
@using AntSK.Domain.Model.Enum
@using AntSK.Domain.Domain.Model.Enum
@page "/setting/model/add"
@page "/setting/model/add/{ModelId}"
@page "/setting/model/addbypath/{ModelPath}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@@ -25,10 +26,10 @@
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<RadioGroup @bind-Value="context.AIModelType">
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
</RadioGroup>
</FormItem>
@if (context.AIModelType == AIModelType.Embedding)
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
</RadioGroup>
</FormItem>
@if (context.AIModelType == AIModelType.Embedding)
{
<FormItem Label="注意事项" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<b>请不要使用不同维度的向量模型,否则会导致无法向量存储</b>
@@ -71,16 +72,26 @@
<InputPassword @bind-Value="@context.ModelKey" Placeholder="APISecret" Size="@InputSize.Large" />
</FormItem>
}
@if (context.AIType == AIType.DashScope)
{
<FormItem Label="API KEY" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入API KEY" @bind-Value="@context.ModelKey" />
</FormItem>
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
</FormItem>
}
@if (context.AIType == AIType.LLamaSharp)
{
<FormItem Label="模型路径" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
<Input Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
<InputGroup>
<AutoComplete Options="_modelFiles" Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
<Button OnClick="()=>_downloadModalVisible=true">从Haggingface下载</Button>
</InputGroup>
</FormItem>
}
@if (context.AIType == AIType.Mock)
{
}
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
<Button Type="primary" OnClick="HandleSubmit">
@@ -95,6 +106,20 @@
</ChildContent>
</PageContainer>
@code {
<Modal @ref="_modal" Visible="_downloadModalVisible" Footer="null" Closable Title="模型下载" OnCancel="OnCancel" DestroyOnClose>
<Flex Gap="10" Vertical>
<InputGroup>
<Input Disabled="_downloadStarted" Placeholder="请输入下载地址" @bind-Value="_downloadUrl" Style="width:80%"></Input>
@if (!_downloadStarted)
{
<Button OnClick="StartDownload">开始</Button>
}
else
{
<Button OnClick="Stop">停止</Button>
}
</InputGroup>
<AntDesign.Progress Percent="_downloadProgress"></AntDesign.Progress>
}
</Flex>
</Modal>

View File

@@ -1,8 +1,12 @@
using AntDesign;
using AntDesign.ProLayout;
using AntSK.Domain.Domain.Model.Enum;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Downloader;
using Microsoft.AspNetCore.Components;
using System.ComponentModel;
namespace AntSK.Pages.Setting.AIModel
{
@@ -10,27 +14,58 @@ namespace AntSK.Pages.Setting.AIModel
{
[Parameter]
public string ModelId { get; set; }
[Parameter]
public string ModelPath { get; set; }
[Inject] protected IAIModels_Repositories _aimodels_Repositories { get; set; }
[Inject] protected MessageService? Message { get; set; }
[Inject] public HttpClient HttpClient { get; set; }
private AIModels _aiModel = new AIModels();
private string _downloadUrl;
private bool _downloadModalVisible;
private double _downloadProgress;
private bool _downloadFinished;
private bool _downloadStarted;
IDownload _download;
private Modal _modal;
string[] _modelFiles;
IEnumerable<string> _menuKeys;
private List<MenuDataItem> menuList = new List<MenuDataItem>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (!string.IsNullOrEmpty(ModelId))
try
{
_aiModel = _aimodels_Repositories.GetFirst(p => p.Id == ModelId);
await base.OnInitializedAsync();
if (!string.IsNullOrEmpty(ModelId))
{
_aiModel = _aimodels_Repositories.GetFirst(p => p.Id == ModelId);
}
//目前只支持gguf的 所以筛选一下
_modelFiles = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory)).Where(p=>p.Contains(".gguf")).ToArray();
if (!string.IsNullOrEmpty(ModelPath))
{
//下载页跳入
_aiModel.AIType = AIType.LLamaSharp;
_downloadModalVisible = true;
_downloadUrl = $"https://hf-mirror.com{ModelPath.Replace("---","/")}";
}
}
catch
{
_ = Message.Error("LLamaSharp.FileDirectory目录配置不正确", 2);
}
}
private void HandleSubmit()
{
if (_aimodels_Repositories.IsAny(p => p.AIModelType == _aiModel.AIModelType && p.EndPoint == _aiModel.EndPoint.ConvertToString() && p.ModelKey == _aiModel.ModelKey && p.ModelName == _aiModel.ModelName))
if (_aimodels_Repositories.IsAny(p => p.Id!=_aiModel.Id.ConvertToString()&& p.AIModelType == _aiModel.AIModelType && p.EndPoint == _aiModel.EndPoint.ConvertToString() && p.ModelKey == _aiModel.ModelKey && p.ModelName == _aiModel.ModelName))
{
_ = Message.Error("模型已存在!", 2);
return;
@@ -69,5 +104,69 @@ namespace AntSK.Pages.Setting.AIModel
{
NavigationManager.NavigateTo("/setting/modellist");
}
private async Task StartDownload()
{
if (string.IsNullOrWhiteSpace(_downloadUrl))
{
return;
}
_download = DownloadBuilder.New()
.WithUrl(_downloadUrl)
.WithDirectory(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory))
.WithConfiguration(new DownloadConfiguration()
{
ParallelCount = 5,
})
.Build();
_download.DownloadProgressChanged += DownloadProgressChanged;
_download.DownloadFileCompleted += DownloadFileCompleted;
_download.DownloadStarted += DownloadStarted;
await _download.StartAsync();
//download.Stop(); // cancel current download
}
private void DownloadProgressChanged(object? sender, DownloadProgressChangedEventArgs e)
{
_downloadProgress = Math.Round( e.ProgressPercentage,2);
InvokeAsync(StateHasChanged);
}
private void DownloadFileCompleted(object? sender, AsyncCompletedEventArgs e)
{
_downloadFinished = true;
_aiModel.ModelName = _download.Package.FileName;
_downloadModalVisible = false;
_downloadStarted = false;
_modelFiles = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), LLamaSharpOption.FileDirectory));
InvokeAsync(StateHasChanged);
}
private void DownloadStarted(object? sender, DownloadStartedEventArgs e)
{
_downloadStarted = true;
InvokeAsync(StateHasChanged);
}
private void OnCancel()
{
if (_downloadStarted)
{
return;
}
_downloadModalVisible = false;
}
private void Stop()
{
_downloadStarted=false;
_download?.Stop();
InvokeAsync(StateHasChanged);
}
}
}

View File

@@ -1,19 +1,63 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/modeldown"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@using AntSK.Domain.Domain.Model.hfmirror;
using AntSK.Domain.Domain.Model.hfmirror
@attribute [Authorize(Roles = "AntSKAdmin")]
<PageContainer Title="模型下载">
<PageContainer Title="模型列表">
<Content>
<div style="text-align: center;">
<Search Placeholder="输入回车"
EnterButton="@("搜索")"
Size="large"
Style="max-width: 522px; width: 100%;"
OnSearch="Search" />
</div>
</Content>
<ChildContent>
<h1>支持LLamaSharp的本地模型 支持gguf类型推荐使用llama或者qwen</h1>
<h1>如果模型加载报内存错误可能是和llama.cpp版本不一致</h1>
<a href="https://hf-mirror.com/models?search=gguf" target="_blank" rel="noopener noreferrer">打开下载地址</a>
<div class="filterCardList">
<AntList TItem="HfModels"
Grid="LayoutModel._listGridType"
DataSource="_modelList">
<ListItem NoFlex>
<Card Hoverable
BodyStyle="padding-bottom: 20px;"
Actions="new[] {
down(()=> Down(context.Id))
}">
<CardMeta>
<TitleTemplate>
@context.Id
</TitleTemplate>
<AvatarTemplate>
<Avatar Size="small" Src="@context.AuthorData.AvatarUrl" />
</AvatarTemplate>
</CardMeta>
<div class="cardItemContent">
<div class="cardInfo">
<div>
<p>Downloads</p>
<p>@context.Downloads.ToString("0,0")</p>
</div>
<div>
<p>Likes</p>
<p>@context.Likes.ToString("0,0")</p>
</div>
</div>
</div>
</Card>
</ListItem>
</AntList>
</div>
</ChildContent>
</PageContainer>
@code {
@code
{
RenderFragment down(Action clickAction) =>@<a key="down" @onclick="@clickAction">下载</a>;
}

View File

@@ -0,0 +1,52 @@
using AntDesign;
using AntSK.Models;
using AntSK.Services;
using DocumentFormat.OpenXml.Office2010.Excel;
using Microsoft.AspNetCore.Components;
using Newtonsoft.Json;
using RestSharp;
using AntSK.Domain.Utils;
using AntSK.Domain.Domain.Model.hfmirror;
namespace AntSK.Pages.Setting.AIModel
{
public partial class ModelDown
{
private readonly ListFormModel _model = new ListFormModel();
private readonly IList<string> _selectCategories = new List<string>();
private List<HfModels> _modelList = new List<HfModels>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
InitData("");
}
private void InitData(string searchKey)
{
var param = searchKey.ConvertToString().Split(" ");
string urlBase = "https://hf-mirror.com/models-json?sort=trending&search=gguf";
if (param.Count() > 0)
{
urlBase += "+" + string.Join("+", param);
}
RestClient client = new RestClient();
RestRequest request = new RestRequest(urlBase, Method.Get);
var response = client.Execute(request);
var model = JsonConvert.DeserializeObject<HfModel>(response.Content);
_modelList = model.models;
}
private async Task Search(string searchKey)
{
InitData(searchKey);
}
private void Down(string modelPath)
{
NavigationManager.NavigateTo($"/setting/modeldown/detail/{modelPath}");
}
}
}

View File

@@ -0,0 +1,46 @@
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
/* stylelint-disable no-duplicate-selectors */
/* stylelint-disable */
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
.filterCardList .ant-card-meta-content {
margin-top: 0;
}
.filterCardList .ant-card-meta-avatar {
font-size: 0;
}
.filterCardList .ant-list .ant-list-item-content-single {
max-width: 100%;
}
.filterCardList .cardInfo {
margin-top: 16px;
margin-left: 40px;
zoom: 1;
}
.filterCardList .cardInfo::before,
.filterCardList .cardInfo::after {
display: table;
content: ' ';
}
.filterCardList .cardInfo::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
.filterCardList .cardInfo > div {
position: relative;
float: left;
width: 50%;
text-align: left;
}
.filterCardList .cardInfo > div p {
margin: 0;
font-size: 24px;
line-height: 32px;
}
.filterCardList .cardInfo > div p:first-child {
margin-bottom: 4px;
color: rgba(0, 0, 0, 0.45);
font-size: 12px;
line-height: 20px;
}

View File

@@ -0,0 +1,55 @@
@namespace AntSK.Pages.Setting.AIModel
@page "/setting/modeldown/detail/{ModelName}/{ModelPath}"
@using AntSK.Services.Auth
@inherits AuthComponentBase
@using Microsoft.AspNetCore.Authorization
@using AntSK.Domain.Domain.Model.hfmirror;
using AntSK.Domain.Domain.Model.hfmirror
@attribute [Authorize(Roles = "AntSKAdmin")]
<div>
<PageContainer Title="模型下载">
<ChildContent>
<div class="standardList">
<Card Class="listCard"
Title="模型列表"
Style="margin-top: 24px;"
BodyStyle="padding: 0 32px 40px 32px">
<Extra>
</Extra>
<ChildContent>
<AntList TItem="HfModelDetail"
DataSource="modelList"
ItemLayout="ListItemLayout.Horizontal">
<ListItem Actions="new[] {
down(()=> Down(context.Path))
}" Style="width:100%">
<div class="listContent" style="width:100%">
<div class="listContentItem" style="width:20%">
<b>名称</b>
<p>@context.Name</p>
</div>
<div class="listContentItem" style="width:20%">
<b>文件大小</b>
<p>@context.Size</p>
</div>
<div class="listContentItem" style="width:20%">
<b>更新时间</b>
<p>@context.Time</p>
</div>
</div>
</ListItem>
</AntList>
</ChildContent>
</Card>
</div>
</ChildContent>
</PageContainer>
</div>
@code
{
RenderFragment down(Action clickAction) =>@<a key="down" @onclick="@clickAction">下载</a>;
}

View File

@@ -0,0 +1,56 @@
using AntSK.Domain.Domain.Model.hfmirror;
using DocumentFormat.OpenXml.EMMA;
using DocumentFormat.OpenXml.Spreadsheet;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Components;
using RestSharp;
namespace AntSK.Pages.Setting.AIModel
{
public partial class ModelDownDetail
{
[Parameter]
public string ModelName { get; set; }
[Parameter]
public string ModelPath { get; set; }
List<HfModelDetail> modelList = new List<HfModelDetail>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
InitData();
}
private void InitData()
{
string urlBase = $"https://hf-mirror.com/{ModelName}/{ModelPath}/tree/main";
RestClient client = new RestClient();
RestRequest request = new RestRequest(urlBase, Method.Get);
var response = client.Execute(request);
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(response.Content);
foreach (var listItem in htmlDocument.DocumentNode.SelectNodes("//li[contains(@class,'grid')]"))
{
var modelNameNode = listItem.SelectSingleNode(".//span[contains(@class,'truncate')]");
var fileSizeNode = listItem.SelectSingleNode(".//a[contains(@class,'text-[0.8rem]')]");
var downloadNode = listItem.SelectSingleNode(".//a[@title='Download file']");
var timeNode = listItem.SelectSingleNode(".//time");
var modelName = modelNameNode?.InnerText.Trim();
var fileSizeInfo = fileSizeNode?.InnerText.Trim().Split(' ');
var fileSize = fileSizeInfo?.Length > 1 ? fileSizeInfo[fileSizeInfo.Length - 2] : null;
var downloadUrl = downloadNode?.GetAttributeValue("href", null)?.Trim();
var time= timeNode?.InnerText.Trim();
modelList.Add(new HfModelDetail() { Name = modelName, Size = string.Join(" ",fileSizeInfo), Path = downloadUrl,Time=time });
}
}
private void Down(string path)
{
NavigationManager.NavigateTo($"/setting/model/addbypath/{path.Replace("?download=true", "").Replace("/", "---")}");
}
}
}

View File

@@ -0,0 +1,186 @@
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
/* stylelint-disable no-duplicate-selectors */
/* stylelint-disable */
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
.standardList .ant-card-head {
border-bottom: none;
}
.standardList .ant-card-head-title {
padding: 24px 0;
line-height: 32px;
}
.standardList .ant-card-extra {
padding: 24px 0;
}
.standardList .ant-list-pagination {
margin-top: 24px;
text-align: right;
}
.standardList .ant-avatar-lg {
width: 48px;
height: 48px;
line-height: 48px;
}
.standardList .headerInfo {
position: relative;
text-align: center;
}
.standardList .headerInfo > span {
display: inline-block;
margin-bottom: 4px;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
line-height: 22px;
}
.standardList .headerInfo > p {
margin: 0;
color: rgba(0, 0, 0, 0.85);
font-size: 24px;
line-height: 32px;
}
.standardList .headerInfo > em {
position: absolute;
top: 0;
right: 0;
width: 1px;
height: 56px;
background-color: #f0f0f0;
}
.standardList .listContent {
font-size: 0;
}
.standardList .listContent .listContentItem {
display: inline-block;
margin-left: 40px;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
vertical-align: middle;
}
.standardList .listContent .listContentItem > span {
line-height: 20px;
}
.standardList .listContent .listContentItem > p {
margin-top: 4px;
margin-bottom: 0;
line-height: 22px;
}
.standardList .extraContentSearch {
width: 272px;
margin-left: 16px;
}
@media screen and (max-width: 480px) {
.standardList .ant-list-item-content {
display: block;
flex: none;
width: 100%;
}
.standardList .ant-list-item-action {
margin-left: 0;
}
.standardList .listContent {
margin-left: 0;
}
.standardList .listContent > div {
margin-left: 0;
}
.standardList .listCard .ant-card-head-title {
overflow: visible;
}
}
@media screen and (max-width: 576px) {
.standardList .extraContentSearch {
width: 100%;
margin-left: 0;
}
.standardList .headerInfo {
margin-bottom: 16px;
}
.standardList .headerInfo > em {
display: none;
}
}
@media screen and (max-width: 768px) {
.standardList .listContent > div {
display: block;
}
.standardList .listContent > div:last-child {
top: 0;
width: 100%;
}
.listCard .ant-radio-group {
display: block;
margin-bottom: 8px;
}
}
@media screen and (max-width: 992px) and (min-width: 768px) {
.standardList .listContent > div {
display: block;
}
.standardList .listContent > div:last-child {
top: 0;
width: 100%;
}
}
@media screen and (max-width: 1200px) {
.standardList .listContent > div {
margin-left: 24px;
}
.standardList .listContent > div:last-child {
top: 0;
}
}
@media screen and (max-width: 1400px) {
.standardList .listContent {
text-align: right;
}
.standardList .listContent > div:last-child {
top: 0;
}
}
.standardListForm .ant-form-item {
margin-bottom: 12px;
}
.standardListForm .ant-form-item:last-child {
margin-bottom: 32px;
padding-top: 4px;
}
.formResult {
width: 100%;
}
.formResult [class^='title'] {
margin-bottom: 8px;
}

View File

@@ -1,6 +1,6 @@
@namespace AntSK.Pages.Setting.AIModel
@using AntSK.Domain.Repositories
@using AntSK.Domain.Model.Enum
@using AntSK.Domain.Domain.Model.Enum
@page "/setting/modellist"
@inject NavigationManager NavigationManager
@using AntSK.Services.Auth

View File

@@ -1,9 +1,9 @@
using AntDesign.ProLayout;
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Common.Map;
using AntSK.Domain.Domain.Model;
using AntSK.Domain.Domain.Other;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Model;
using AntSK.Domain.Options;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
@@ -13,6 +13,7 @@ using LLama.Native;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text.Encodings.Web;
@@ -44,8 +45,8 @@ builder.Services.AddScoped(sp => new HttpClient
builder.Services.Configure<ProSettings>(builder.Configuration.GetSection("ProSettings"));
builder.Services.AddServicesFromAssemblies("AntSK");
builder.Services.AddServicesFromAssemblies("AntSK.Domain");
builder.Services.AddSingleton(sp => new FunctionService(sp, [typeof(AntSK.App).Assembly, typeof(AntSK.Domain.Common.AntSkFunctionAttribute).Assembly]));
builder.Services.AddSingleton<FunctionTest>();
builder.Services.AddSingleton(sp => new FunctionService(sp, [typeof(AntSK.App).Assembly]));
builder.Services.AddScoped<FunctionTest>();
builder.Services.AddSwaggerGen(c =>
{
@@ -70,6 +71,26 @@ builder.Services.AddSwaggerGen(c =>
return actionVersion.Any(v => v == docName);
return version.Any(v => v == docName);
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Description = "Directly enter bearer {token} in the box below (note that there is a space between bearer and token)",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference()
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
}, Array.Empty<string>()
}
});
});
//Mapper
builder.Services.AddMapper();
@@ -80,6 +101,7 @@ builder.Services.AddBackgroundTaskBroker().AddHandler<ImportKMSTaskReq, BackGrou
builder.Configuration.GetSection("DBConnection").Get<DBConnectionOption>();
builder.Configuration.GetSection("Login").Get<LoginOption>();
builder.Configuration.GetSection("LLamaSharp").Get<LLamaSharpOption>();
builder.Configuration.GetSection("KernelMemory").Get<KernelMemoryOption>();
if (LLamaSharpOption.RunType.ToUpper() == "CPU")
{
NativeLibraryConfig
@@ -111,6 +133,7 @@ if (!app.Environment.IsDevelopment())
app.UseStaticFiles();
InitDB(app);
LoadFun(app);
app.UseRouting();
@@ -140,7 +163,30 @@ void InitDB(WebApplication app)
_repository.GetDB().CodeFirst.InitTables(typeof(Users));
_repository.GetDB().CodeFirst.InitTables(typeof(Apis));
_repository.GetDB().CodeFirst.InitTables(typeof(AIModels));
_repository.GetDB().CodeFirst.InitTables(typeof(Funs));
//创建vector插件如果数据库没有则需要提供支持向量的数据库
_repository.GetDB().Ado.ExecuteCommandAsync($"CREATE EXTENSION IF NOT EXISTS vector;");
}
}
void LoadFun(WebApplication app)
{
try
{
using (var scope = app.Services.CreateScope())
{
//codefirst 创建表
var funRep = scope.ServiceProvider.GetRequiredService<IFuns_Repositories>();
var functionService = scope.ServiceProvider.GetRequiredService<FunctionService>();
var funs= funRep.GetList();
foreach (var fun in funs)
{
functionService.FuncLoad(fun.Path);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}

View File

@@ -1,5 +1,5 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Domain.Utils;
using Newtonsoft.Json;
using System.Text;

View File

@@ -1,6 +1,6 @@
using AntSK.Domain.Common.DependencyInjection;
using AntSK.Domain.Domain.Dto;
using AntSK.Domain.Domain.Interface;
using AntSK.Domain.Domain.Model.Dto.OpenAPI;
using AntSK.Domain.Repositories;
using AntSK.Domain.Utils;
using Microsoft.KernelMemory;
@@ -8,6 +8,7 @@ using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Newtonsoft.Json;
using System.Text;
using System.Text.RegularExpressions;
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
namespace AntSK.Services.OpenApi
@@ -20,16 +21,18 @@ namespace AntSK.Services.OpenApi
[ServiceDescription(typeof(IOpenApiService), ServiceLifetime.Scoped)]
public class OpenApiService(
IApps_Repositories _apps_Repositories,
IKmss_Repositories _kmss_Repositories,
IKmsDetails_Repositories _kmsDetails_Repositories,
IKernelService _kernelService,
IKMService _kMService,
IChatService _chatService
) : IOpenApiService
) : IOpenApiService
{
public async Task Chat(OpenAIModel model, string sk, HttpContext HttpContext)
{
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == sk);
string headerValue = sk;
Regex regex = new Regex(@"Bearer (.*)");
Match match = regex.Match(headerValue);
string token = match.Groups[1].Value;
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == token);
if (app.IsNotNull())
{
string msg = await HistorySummarize(app, model);
@@ -41,7 +44,8 @@ namespace AntSK.Services.OpenApi
{
OpenAIStreamResult result1 = new OpenAIStreamResult();
result1.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result1.choices = new List<StreamChoicesModel>() { new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
result1.choices = new List<StreamChoicesModel>()
{ new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
await SendChatStream(HttpContext, result1, app, msg);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result1));
@@ -52,12 +56,14 @@ namespace AntSK.Services.OpenApi
{
OpenAIResult result2 = new OpenAIResult();
result2.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result2.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result2.choices = new List<ChoicesModel>()
{ new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result2.choices[0].message.content = await SendChat(msg, app);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result2));
await HttpContext.Response.CompleteAsync();
}
break;
case "kms":
@@ -66,7 +72,8 @@ namespace AntSK.Services.OpenApi
{
OpenAIStreamResult result3 = new OpenAIStreamResult();
result3.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result3.choices = new List<StreamChoicesModel>() { new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
result3.choices = new List<StreamChoicesModel>()
{ new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
await SendKmsStream(HttpContext, result3, app, msg);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result3));
@@ -76,12 +83,14 @@ namespace AntSK.Services.OpenApi
{
OpenAIResult result4 = new OpenAIResult();
result4.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
result4.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result4.choices = new List<ChoicesModel>()
{ new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
result4.choices[0].message.content = await SendKms(msg, app);
HttpContext.Response.ContentType = "application/json";
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result4));
await HttpContext.Response.CompleteAsync();
}
break;
}
}
@@ -91,7 +100,6 @@ namespace AntSK.Services.OpenApi
{
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
var chatResult = _chatService.SendChatByAppAsync(app, msg, "");
int i = 0;
await foreach (var content in chatResult)
{
result.choices[0].delta.content = content.ConvertToString();
@@ -111,7 +119,6 @@ namespace AntSK.Services.OpenApi
/// <summary>
/// 发送普通对话
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
@@ -123,30 +130,31 @@ namespace AntSK.Services.OpenApi
//如果模板为空,给默认提示词
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
}
var _kernel = _kernelService.GetKernelByApp(app);
var temperature = app.Temperature / 100;//存的是0~100需要缩小
var temperature = app.Temperature / 100; //存的是0~100需要缩小
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
_kernelService.ImportFunctionsByApp(app, _kernel);
settings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
var promptTemplateFactory = new KernelPromptTemplateFactory();
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
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: new KernelArguments() { ["input"] = msg });
var chatResult =await _kernel.InvokeAsync(function: func, arguments: new KernelArguments() { ["input"] = msg });
if (chatResult.IsNotNull())
{
string answers = chatResult.GetValue<string>();
result = answers;
}
return result;
}
private async Task SendKmsStream(HttpContext HttpContext, OpenAIStreamResult result, Apps app, string msg)
{
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
var chatResult = _chatService.SendKmsByAppAsync(app, msg, "");
var chatResult = _chatService.SendKmsByAppAsync(app, msg,"", "");
int i = 0;
await foreach (var content in chatResult)
{
@@ -167,35 +175,23 @@ namespace AntSK.Services.OpenApi
/// <summary>
/// 发送知识库问答
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <returns></returns>
private async Task<string> SendKms(string msg, Apps app)
{
var _kernel = _kernelService.GetKernelByApp(app);
var _memory = _kMService.GetMemoryByKMS(app.KmsIdList.Split(",").FirstOrDefault());
string result = "";
//知识库问答
var filters = new List<MemoryFilter>();
var _kernel = _kernelService.GetKernelByApp(app);
var kmsidList = app.KmsIdList.Split(",");
foreach (var kmsid in kmsidList)
var relevantSource = await _kMService.GetRelevantSourceList(app.KmsIdList, msg);
var dataMsg = new StringBuilder();
if (relevantSource.Any())
{
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
}
var xlresult = await _memory.SearchAsync(msg, index: "kms", filters: filters);
string dataMsg = "";
if (xlresult != null)
{
foreach (var item in xlresult.Results)
foreach (var item in relevantSource)
{
foreach (var part in item.Partitions)
{
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance * 100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
}
dataMsg.AppendLine(item.ToString());
}
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
var chatResult = await _kernel.InvokeAsync(function: jsonFun,
arguments: new KernelArguments() { ["doc"] = dataMsg, ["history"] = "", ["questions"] = msg });
@@ -205,14 +201,15 @@ namespace AntSK.Services.OpenApi
result = answers;
}
}
return result;
}
/// <summary>
/// 历史会话的会话总结
/// </summary>
/// <param name="questions"></param>
/// <param name="msg"></param>
/// <param name="app"></param>
/// <param name="model"></param>
/// <returns></returns>
private async Task<string> HistorySummarize(Apps app, OpenAIModel model)
{
@@ -233,7 +230,8 @@ namespace AntSK.Services.OpenApi
}
else
{
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}"; ;
var msg = $"history{history.ToString()}{Environment.NewLine} user{questions}";
;
return msg;
}
}

View File

@@ -7,6 +7,7 @@
"Microsoft.Hosting.Lifetime": "Information"
}
},
"urls":"http://*:5000",
"ProSettings": {
"NavTheme": "light",
"Layout": "side",
@@ -33,9 +34,10 @@
"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"
"RunType": "GPU",
"Chat": "D:\\git\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"Embedding": "D:\\git\\AntBlazor\\model\\qwen1_5-1_8b-chat-q8_0.gguf",
"FileDirectory": "D:\\git\\AntBlazor\\model\\\\"
},
"Login": {
"User": "admin",

View File

@@ -1,16 +1,14 @@
using AntSK.Domain.Common;
using AntSK.Domain.Repositories;
using System.ComponentModel;
namespace AntSK.plugins.Functions
{
public class FunctionTest
public class FunctionTest(IAIModels_Repositories Repository)
{
/// <summary>
/// 获取订单信息
/// </summary>
/// <param name="id">订单号</param>
/// <returns>订单信息</returns>
[AntSkFunction]
public string GetOrder(int id)
[Description("AntSK:获取订单信息")]
[return: Description("订单信息")]
public string GetOrder([Description("订单号")] string id)
{
return $"""
订单ID: {id}
@@ -20,5 +18,15 @@ namespace AntSK.plugins.Functions
收货地址:上海市黄浦区
""";
}
[Description("AntSK:获取模型")]
[return: Description("模型列表")]
public string GetModels()
{
var models = Repository.GetList();
return string.Join(",", models.Select(x => x.ModelName));
}
}
}

View File

@@ -1,11 +1,11 @@
Facts:
====
{{$doc}}
--------------------------
History:{{$history}}
--------------------------
Question: {{$questions}}
--------------------------
Given only the facts above, provide a comprehensive/detailed answer.
You don't know where the knowledge comes from, just answer.
Answer in the same language as the question,
If you don't have sufficient information, reply with '知识库未搜索到相关内容'.
History:{{$history}}
Question: {{$questions}}
Answer:

View File

@@ -0,0 +1,30 @@
{
"schema": 1,
"type": "completion",
"description": "知识库问答",
"completion": {
"temperature": 0.0,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "doc",
"description": "背景文档。",
"defaultValue": ""
},
{
"name": "history",
"description": "历史聊天记录。",
"defaultValue": ""
},
{
"name": "questions",
"description": "用户问题。",
"defaultValue": ""
}
]
}
}

View File

@@ -0,0 +1,17 @@
使用<data></data>标记的内容作为你的知识:
<data>
{{$doc}}
</data>
--------------------------
回答要求:
- 如果你不清楚答案,你需要澄清
- 避免提及你是从<data></data>获取的知识
- 保持答案与<data></data>众描述一致
- 使用Markdown语法优化回答格式。
- 如果Markdown有图片则正常显示
--------------------------
历史聊天记录:{{$history}}
--------------------------
用户问题: {{$questions}}

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