mirror of
https://github.com/AIDotNet/AntSK.git
synced 2026-02-18 14:30:18 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e754b4732 | ||
|
|
48a9fcfabf | ||
|
|
7ae2b9ac3b | ||
|
|
a6bfe3c69b | ||
|
|
6b146f4750 | ||
|
|
1387e716d1 | ||
|
|
fadc350047 | ||
|
|
8af4994a7d | ||
|
|
b505b61bfe | ||
|
|
5d086c9383 | ||
|
|
dd0e367dc8 | ||
|
|
7110eea912 | ||
|
|
154e67ef98 | ||
|
|
4a04423373 | ||
|
|
470ea50ebb | ||
|
|
2a30f3f221 | ||
|
|
315f58fdba | ||
|
|
f027682175 | ||
|
|
2618dffcd6 | ||
|
|
ee42d5870b | ||
|
|
27500b1e08 | ||
|
|
4d71a98724 | ||
|
|
b8ba0ab391 | ||
|
|
b589612913 | ||
|
|
ec4b440469 | ||
|
|
adbecb3b25 | ||
|
|
277aacc34d | ||
|
|
dba98f7968 | ||
|
|
a2b0f3f3c2 | ||
|
|
b5e527afdb |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -337,8 +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
|
||||
/src/AntSK/AntSK.xml
|
||||
118
README.en.md
118
README.en.md
@@ -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
|
||||
|
||||
@@ -3,7 +3,7 @@ version: '3.8'
|
||||
services:
|
||||
antsk:
|
||||
container_name: antsk
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.9
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
|
||||
ports:
|
||||
- 5000:5000
|
||||
networks:
|
||||
|
||||
@@ -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.9
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.2.1
|
||||
ports:
|
||||
- 5000:5000
|
||||
networks:
|
||||
|
||||
@@ -112,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>
|
||||
注册默认插件
|
||||
@@ -217,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>
|
||||
温度
|
||||
@@ -247,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>
|
||||
文件名称
|
||||
@@ -719,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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,24 @@
|
||||
using AntSK.Domain.Domain.Model.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/AntSK.Domain/Domain/Model/Constant/KmsConstantcs.cs
Normal file
15
src/AntSK.Domain/Domain/Model/Constant/KmsConstantcs.cs
Normal 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="知识库未搜索到相关内容";
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,13 @@
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace AntSK.Domain.Domain.Model.Dto
|
||||
{
|
||||
public class RelevantSource
|
||||
@@ -12,5 +7,10 @@ namespace AntSK.Domain.Domain.Model.Dto
|
||||
|
||||
public string Text { get; set; }
|
||||
public float Relevance { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[file:{SourceName};Relevance:{(Relevance * 100):F2}%]:{Text}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
namespace AntSK.Domain.Domain.Model.Enum
|
||||
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,
|
||||
Mock = 5,
|
||||
|
||||
[Display(Name = "灵积大模型")]
|
||||
DashScope = 5,
|
||||
|
||||
[Display(Name = "模拟输出")]
|
||||
Mock = 100,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
23
src/AntSK.Domain/Domain/Model/Fun/FunDto.cs
Normal file
23
src/AntSK.Domain/Domain/Model/Fun/FunDto.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,15 @@ 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 Microsoft.KernelMemory;
|
||||
using Markdig;
|
||||
using AntSK.Domain.Domain.Model;
|
||||
using AntSK.Domain.Domain.Model.Dto;
|
||||
using AntSK.Domain.Domain.Model.Constant;
|
||||
using DocumentFormat.OpenXml.Drawing;
|
||||
using System.Reflection.Metadata;
|
||||
using Microsoft.KernelMemory;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
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
|
||||
{
|
||||
@@ -12,7 +17,8 @@ namespace AntSK.Domain.Domain.Service
|
||||
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)
|
||||
{
|
||||
@@ -20,6 +26,7 @@ namespace AntSK.Domain.Domain.Service
|
||||
_methodInfos = [];
|
||||
_serviceProvider = serviceProvider;
|
||||
_assemblies = assemblies;
|
||||
loadContext = new AssemblyLoadContext("AntSKLoadContext", true);
|
||||
}
|
||||
|
||||
public Dictionary<string, MethodInfo> Functions => _methodCache;
|
||||
@@ -43,7 +50,22 @@ namespace AntSK.Domain.Domain.Service
|
||||
markedMethods.AddRange(type.GetMethods().Where(m =>
|
||||
{
|
||||
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
|
||||
return da != null && da.Description == "AntSK";
|
||||
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");
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -52,19 +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, method);
|
||||
|
||||
var xmlCommentHelper = new XmlCommentHelper();
|
||||
xmlCommentHelper.LoadAll();
|
||||
|
||||
var description = xmlCommentHelper.GetMethodComment(method);
|
||||
var dict = xmlCommentHelper.GetParameterComments(method);
|
||||
|
||||
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType, dict[x.Name])).ToArray();
|
||||
var returnType = xmlCommentHelper.GetMethodReturnComment(method);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
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;
|
||||
|
||||
@@ -1,29 +1,75 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ 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
|
||||
{
|
||||
@@ -95,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;
|
||||
@@ -113,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))
|
||||
{
|
||||
//开启自动插件调用
|
||||
@@ -124,14 +145,24 @@ 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.ConvertToString().Split("\n"))
|
||||
@@ -143,13 +174,9 @@ namespace AntSK.Domain.Domain.Service
|
||||
}
|
||||
}
|
||||
//这里应该还要处理一次参数提取,等后面再迭代
|
||||
foreach (var query in api.Query.ConvertToString().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;
|
||||
@@ -158,15 +185,22 @@ 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.ConvertToString().Split("\n"))
|
||||
@@ -178,7 +212,7 @@ namespace AntSK.Domain.Domain.Service
|
||||
}
|
||||
}
|
||||
//这里应该还要处理一次参数提取,等后面再迭代
|
||||
request.AddJsonBody(api.JsonBody.ConvertToString());
|
||||
request.AddJsonBody(jsonBody.ConvertToString());
|
||||
var result = client.Execute(request);
|
||||
return result.Content;
|
||||
}
|
||||
@@ -186,19 +220,27 @@ 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();
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
|
||||
foreach (var func in _functionService.Functions)
|
||||
{
|
||||
@@ -208,11 +250,10 @@ namespace AntSK.Domain.Domain.Service
|
||||
var parameters = methodInfo.Parameters.Select(x => new KernelParameterMetadata(x.ParameterName) { ParameterType = x.ParameterType, Description = x.Description });
|
||||
var returnType = new KernelReturnParameterMetadata() { ParameterType = methodInfo.ReturnType.ParameterType, Description = methodInfo.ReturnType.Description };
|
||||
var target = ActivatorUtilities.CreateInstance(scope.ServiceProvider, func.Value.DeclaringType);
|
||||
apiFunctions.Add(_kernel.CreateFunctionFromMethod(func.Value, target, func.Key, methodInfo.Description, parameters, returnType));
|
||||
functions.Add(_kernel.CreateFunctionFromMethod(func.Value, target, func.Key, methodInfo.Description, parameters, returnType));
|
||||
}
|
||||
}
|
||||
}
|
||||
_kernel.ImportPluginFromFunctions("AntSkFunctions", apiFunctions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -223,7 +264,7 @@ namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
|
||||
//kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
|
||||
kernel.ImportPluginFromPromptDirectory(Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
|
||||
kernel.ImportPluginFromPromptDirectory(System.IO.Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/AntSK.Domain/Repositories/AI/Fun/Funs.cs
Normal file
20
src/AntSK.Domain/Repositories/AI/Fun/Funs.cs
Normal 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; }
|
||||
|
||||
}
|
||||
}
|
||||
11
src/AntSK.Domain/Repositories/AI/Fun/Funs_Repositories.cs
Normal file
11
src/AntSK.Domain/Repositories/AI/Fun/Funs_Repositories.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
public interface IFuns_Repositories : IRepository<Funs>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
src/AntSK.Test/AntSK.Test.csproj
Normal file
8
src/AntSK.Test/AntSK.Test.csproj
Normal 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
22
src/AntSK.Test/Class1.cs
Normal 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 知识库/智能体项目
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -23,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>
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -9,26 +9,29 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/AntSK/Controllers/TestFunctionCallController.cs
Normal file
24
src/AntSK/Controllers/TestFunctionCallController.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
|
||||
@@ -44,15 +44,17 @@ namespace AntSK.Pages.AppPage
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<a href="@( NavigationManager.BaseUri + "openchat/" + AppId)" target="_blank">分享使用</a>
|
||||
</TitleTemplate>
|
||||
<Body>
|
||||
<div id="scrollDiv" style="height: calc(75vh - 170px); 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)
|
||||
@@ -32,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)" />
|
||||
@@ -58,13 +68,27 @@
|
||||
</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>
|
||||
@@ -79,61 +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;
|
||||
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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,7 @@ 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;
|
||||
@@ -18,31 +13,20 @@ 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:{Environment.NewLine}{history.ToString()}{Environment.NewLine}{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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
<Text>
|
||||
2、支持国产信创数据库
|
||||
</Text>
|
||||
<br
|
||||
<br>
|
||||
<Text>
|
||||
3、支持信创环境运行
|
||||
</Text>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
86
src/AntSK/Pages/Plugin/FunPage/FunList.razor
Normal file
86
src/AntSK/Pages/Plugin/FunPage/FunList.razor
Normal 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
|
||||
{
|
||||
|
||||
}
|
||||
144
src/AntSK/Pages/Plugin/FunPage/FunList.razor.cs
Normal file
144
src/AntSK/Pages/Plugin/FunPage/FunList.razor.cs
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
132
src/AntSK/Pages/Plugin/FunPage/FunList.razor.css
Normal file
132
src/AntSK/Pages/Plugin/FunPage/FunList.razor.css
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,15 @@
|
||||
<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">
|
||||
@@ -79,7 +88,6 @@
|
||||
<AutoComplete Options="_modelFiles" Placeholder="请输入模型路径" @bind-Value="@context.ModelName" />
|
||||
<Button OnClick="()=>_downloadModalVisible=true">从Haggingface下载</Button>
|
||||
</InputGroup>
|
||||
|
||||
</FormItem>
|
||||
}
|
||||
@if (context.AIType == AIType.Mock)
|
||||
|
||||
@@ -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,7 +45,7 @@ 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(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);
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,10 @@ 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)
|
||||
{
|
||||
@@ -46,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));
|
||||
@@ -57,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":
|
||||
@@ -71,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));
|
||||
@@ -81,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;
|
||||
}
|
||||
}
|
||||
@@ -96,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();
|
||||
@@ -116,7 +119,6 @@ namespace AntSK.Services.OpenApi
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
@@ -128,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)
|
||||
{
|
||||
@@ -172,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 });
|
||||
@@ -210,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)
|
||||
{
|
||||
@@ -238,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"urls":"http://*:5000",
|
||||
"ProSettings": {
|
||||
"NavTheme": "light",
|
||||
"Layout": "side",
|
||||
@@ -34,9 +35,9 @@
|
||||
},
|
||||
"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",
|
||||
"FileDirectory": "D:\\Code\\AI\\AntBlazor\\model\\"
|
||||
"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",
|
||||
|
||||
@@ -6,13 +6,9 @@ namespace AntSK.plugins.Functions
|
||||
{
|
||||
public class FunctionTest(IAIModels_Repositories Repository)
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取订单信息
|
||||
/// </summary>
|
||||
/// <param name="id">订单号</param>
|
||||
/// <returns>订单信息</returns>
|
||||
[Description("AntSK")]
|
||||
public string GetOrder(int id)
|
||||
[Description("AntSK:获取订单信息")]
|
||||
[return: Description("订单信息")]
|
||||
public string GetOrder([Description("订单号")] string id)
|
||||
{
|
||||
return $"""
|
||||
订单ID: {id}
|
||||
@@ -23,11 +19,10 @@ namespace AntSK.plugins.Functions
|
||||
""";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模型
|
||||
/// </summary>
|
||||
/// <returns>模型列表</returns>
|
||||
[Description("AntSK")]
|
||||
|
||||
|
||||
[Description("AntSK:获取模型")]
|
||||
[return: Description("模型列表")]
|
||||
public string GetModels()
|
||||
{
|
||||
var models = Repository.GetList();
|
||||
|
||||
@@ -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:
|
||||
30
src/AntSK/plugins/KMSPlugin/Ask1/config.json
Normal file
30
src/AntSK/plugins/KMSPlugin/Ask1/config.json
Normal 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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
17
src/AntSK/plugins/KMSPlugin/Ask1/skprompt.txt
Normal file
17
src/AntSK/plugins/KMSPlugin/Ask1/skprompt.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
使用<data></data>标记的内容作为你的知识:
|
||||
<data>
|
||||
{{$doc}}
|
||||
</data>
|
||||
--------------------------
|
||||
回答要求:
|
||||
- 如果你不清楚答案,你需要澄清
|
||||
- 避免提及你是从<data></data>获取的知识
|
||||
- 保持答案与<data></data>众描述一致
|
||||
- 使用Markdown语法优化回答格式。
|
||||
- 如果Markdown有图片则正常显示
|
||||
--------------------------
|
||||
|
||||
历史聊天记录:{{$history}}
|
||||
--------------------------
|
||||
用户问题: {{$questions}}
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
"path": "/plugins/apilist",
|
||||
"name": "Api管理",
|
||||
"key": "plugins.apilist"
|
||||
},
|
||||
{
|
||||
"path": "/plugins/funlist",
|
||||
"name": "函数管理",
|
||||
"key": "plugins.funlist"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.4.0" />
|
||||
<PackageReference Include="Cnblogs.KernelMemory.AI.DashScope" Version="0.1.0" />
|
||||
<PackageReference Include="Cnblogs.SemanticKernel.Connectors.DashScope" Version="0.2.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.6.2" />
|
||||
<PackageReference Include="Sdcb.SparkDesk" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user