mirror of
https://github.com/AIDotNet/AntSK.git
synced 2026-02-21 09:05:35 +08:00
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4c804c4ac | ||
|
|
c3003f8c3a | ||
|
|
66a2e45029 | ||
|
|
b0c889c336 | ||
|
|
f470b1e555 | ||
|
|
4965983ae1 | ||
|
|
2f2b4e2f3c | ||
|
|
16c739e37f | ||
|
|
f4d51a73cd | ||
|
|
6cc29e4167 | ||
|
|
324e1de84f | ||
|
|
551bdf4802 | ||
|
|
761d9069f6 | ||
|
|
7a15b0f444 | ||
|
|
a185ee4841 | ||
|
|
7213a75212 | ||
|
|
4823977098 | ||
|
|
fa74b84ce3 | ||
|
|
7423b23d3c | ||
|
|
033ded08c7 | ||
|
|
6b397c86d2 | ||
|
|
9730f12c56 | ||
|
|
92c7a80c2e | ||
|
|
64029de11a | ||
|
|
ae363ebad6 | ||
|
|
7b6478b7f5 | ||
|
|
aef7f58c6d | ||
|
|
52450efeb3 | ||
|
|
74d218d2d5 | ||
|
|
f4a60b28ec | ||
|
|
798593bad3 | ||
|
|
778e76b00b | ||
|
|
55778562ae | ||
|
|
66b5b42952 | ||
|
|
3ff8a4f51b | ||
|
|
b2a46db524 | ||
|
|
8d66f4f34d | ||
|
|
199b546d56 | ||
|
|
bea6e5ef53 | ||
|
|
3c17dc9943 | ||
|
|
6d693ed340 | ||
|
|
f86b7aee95 | ||
|
|
fca2d98d51 | ||
|
|
2f3d3d7b55 | ||
|
|
5250817d21 | ||
|
|
6f966590d9 | ||
|
|
da16cf73c9 | ||
|
|
999cda2fc2 | ||
|
|
28f5be071a | ||
|
|
4ad8fb69c7 | ||
|
|
2cb5ab7f98 | ||
|
|
97b87f2adb | ||
|
|
d23c263f9f | ||
|
|
7f003afa2b | ||
|
|
53d52dd17f | ||
|
|
036d57ef05 | ||
|
|
b25e429687 | ||
|
|
a3f87bf123 | ||
|
|
2cab253c4a | ||
|
|
ed119f02e2 | ||
|
|
f178fc16e9 | ||
|
|
e4b2071689 | ||
|
|
146df7ed1d | ||
|
|
7e8db0a3f8 | ||
|
|
e098922219 | ||
|
|
16ca024c22 | ||
|
|
a886e9dfb3 | ||
|
|
5d6114c9ec | ||
|
|
e3b2e1f434 | ||
|
|
6145347d9b | ||
|
|
49e694cbdc | ||
|
|
f860229993 | ||
|
|
f5d93baa17 | ||
|
|
76f58c43b0 | ||
|
|
db29fcc867 | ||
|
|
2f361c23c7 | ||
|
|
4e7ac6eb4b | ||
|
|
58bca689c8 | ||
|
|
e1b2aee33c | ||
|
|
7a6e8525c5 | ||
|
|
b8db68bed1 | ||
|
|
1072f47467 | ||
|
|
cfb1d858c6 | ||
|
|
8e0e75aee6 | ||
|
|
1479c942e2 | ||
|
|
63b02b889c | ||
|
|
5a1de0e2cf | ||
|
|
ad2a402521 | ||
|
|
a14af93afc | ||
|
|
90630bca1d | ||
|
|
bf1db38e2e | ||
|
|
88e23768c6 | ||
|
|
ea8366ca92 | ||
|
|
8e1f07415f | ||
|
|
0b62e773eb | ||
|
|
1c4b1e2d2f | ||
|
|
0e53dd3854 | ||
|
|
c2fa13b6ab | ||
|
|
837c9a1444 | ||
|
|
08ec4f6b7e | ||
|
|
f5a8076da3 | ||
|
|
cf8c935694 | ||
|
|
67fd7d952a | ||
|
|
b7a3ad6fe7 | ||
|
|
0dabd2ee58 | ||
|
|
e841fc6282 | ||
|
|
ab45a46fc0 | ||
|
|
34c06b1dd1 | ||
|
|
50415b12c0 | ||
|
|
61509154c6 | ||
|
|
3d5be56089 | ||
|
|
a0da9c223b | ||
|
|
172755081f | ||
|
|
1da43f2cee | ||
|
|
8b77ea4438 | ||
|
|
b6039c0da3 | ||
|
|
1cda01d5b3 | ||
|
|
6bd2ef4131 | ||
|
|
96e1ca6b34 | ||
|
|
6620a74447 | ||
|
|
fb7a85b9cf | ||
|
|
b7532676c5 | ||
|
|
8cd6940311 | ||
|
|
2de851f5b1 | ||
|
|
e8fd055349 | ||
|
|
7b92373f54 | ||
|
|
03b937432f | ||
|
|
bd9798ad17 | ||
|
|
030abf1059 | ||
|
|
91e633700a | ||
|
|
6eb034967c |
@@ -5,18 +5,29 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DocumentationFile>AntSK.Domain.xml</DocumentationFile>
|
||||
<NoWarn>CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="8.1.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="LLamaSharp.kernel-memory" Version="0.10.0" />
|
||||
<PackageReference Include="LLamaSharp.semantic-kernel" Version="0.10.0" />
|
||||
<PackageReference Include="MarkdownSharp" Version="2.0.5" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.137" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||
<PackageReference Include="RestSharp" Version="110.2.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.4.0-alpha" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.30.240227.1" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.30.240227.1" />
|
||||
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.26.240121.1" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.26.240121.1" />
|
||||
<PackageReference Include="LLamaSharp" Version="0.10.0" />
|
||||
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.10.0" />
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -32,6 +32,36 @@
|
||||
瞬时
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Domain.Service.KernelService.GetKernel(System.String,System.String)">
|
||||
<summary>
|
||||
获取kernel实例,依赖注入不好按每个用户去Import不同的插件,所以每次new一个新的kernel
|
||||
</summary>
|
||||
<param name="modelId"></param>
|
||||
<param name="apiKey"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Domain.Service.KernelService.ImportFunctionsByApp(AntSK.Domain.Repositories.Apps,Microsoft.SemanticKernel.Kernel)">
|
||||
<summary>
|
||||
根据app配置的插件,导入插件
|
||||
</summary>
|
||||
<param name="app"></param>
|
||||
<param name="_kernel"></param>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Domain.Service.KernelService.RegisterPluginsWithKernel(Microsoft.SemanticKernel.Kernel)">
|
||||
<summary>
|
||||
注册默认插件
|
||||
</summary>
|
||||
<param name="kernel"></param>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Domain.Service.KernelService.HistorySummarize(Microsoft.SemanticKernel.Kernel,System.String,System.String)">
|
||||
<summary>
|
||||
会话总结
|
||||
</summary>
|
||||
<param name="_kernel"></param>
|
||||
<param name="questions"></param>
|
||||
<param name="history"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Map.MapperExtend.ToDTOList``1(System.Object)">
|
||||
<summary>
|
||||
Entity集合转DTO集合
|
||||
@@ -57,6 +87,11 @@
|
||||
<param name="result"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Model.MessageInfo.IsSend">
|
||||
<summary>
|
||||
发送是true 接收是false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Model.PageList`1.PageIndex">
|
||||
<summary>
|
||||
当前页,从1开始
|
||||
@@ -82,6 +117,46 @@
|
||||
pg链接字符串
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.Name">
|
||||
<summary>
|
||||
接口名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.Describe">
|
||||
<summary>
|
||||
接口描述
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.Url">
|
||||
<summary>
|
||||
接口地址
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.Method">
|
||||
<summary>
|
||||
请求方法
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.Query">
|
||||
<summary>
|
||||
QueryString参数
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.JsonBody">
|
||||
<summary>
|
||||
jsonBody 实体
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.InputPrompt">
|
||||
<summary>
|
||||
入参提示词
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apis.OutputPrompt">
|
||||
<summary>
|
||||
返回提示词
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.Name">
|
||||
<summary>
|
||||
名称
|
||||
@@ -92,16 +167,41 @@
|
||||
描述
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.Icon">
|
||||
<summary>
|
||||
图标
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.Type">
|
||||
<summary>
|
||||
类型
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.Temperature">
|
||||
<summary>
|
||||
温度
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.Prompt">
|
||||
<summary>
|
||||
提示词
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.ApiFunctionList">
|
||||
<summary>
|
||||
插件列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.KmsIdList">
|
||||
<summary>
|
||||
知识库ID列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.SecretKey">
|
||||
<summary>
|
||||
API调用秘钥
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.KmsDetails.FileName">
|
||||
<summary>
|
||||
文件名称
|
||||
@@ -142,6 +242,21 @@
|
||||
会话模型
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Kmss.MaxTokensPerParagraph">
|
||||
<summary>
|
||||
每个段落的最大标记数。
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Kmss.MaxTokensPerLine">
|
||||
<summary>
|
||||
每行,也就是每句话的最大标记数
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Kmss.OverlappingTokens">
|
||||
<summary>
|
||||
段落之间重叠标记的数量。
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Repositories.Base.Repository`1.GetList">
|
||||
<summary>
|
||||
获取所有list
|
||||
@@ -399,6 +514,51 @@
|
||||
sqlserver连接
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.AIModels.AIModelType">
|
||||
<summary>
|
||||
模型类型
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.AIModels.EndPoint">
|
||||
<summary>
|
||||
模型地址
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.AIModels.ModelName">
|
||||
<summary>
|
||||
模型名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.AIModels.ModelKey">
|
||||
<summary>
|
||||
模型秘钥
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Users.No">
|
||||
<summary>
|
||||
工号,用于登陆
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Users.Password">
|
||||
<summary>
|
||||
密码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Users.Name">
|
||||
<summary>
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Users.Describe">
|
||||
<summary>
|
||||
备注
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Users.MenuRole">
|
||||
<summary>
|
||||
菜单权限
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.ConvertUtils.IsNull(System.Object)">
|
||||
<summary>
|
||||
判断是否为空,为空返回true
|
||||
@@ -489,5 +649,11 @@
|
||||
<param name="stream"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Domain.Utils.RepoFiles.SamplePluginsPath">
|
||||
<summary>
|
||||
Scan the local folders from the repo, looking for "samples/plugins" folder.
|
||||
</summary>
|
||||
<returns>The full path to samples/plugins</returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using AntSK.Domain.Domain.Dto;
|
||||
using Microsoft.KernelMemory.Configuration;
|
||||
using Microsoft.KernelMemory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -9,6 +11,7 @@ namespace AntSK.Domain.Domain.Interface
|
||||
{
|
||||
public interface IKMService
|
||||
{
|
||||
MemoryServerless GetMemory(SearchClientConfig searchClientConfig = null, TextPartitioningOptions textPartitioningOptions = null);
|
||||
Task<List<KMFile>> GetDocumentByFileID(string fileid);
|
||||
}
|
||||
}
|
||||
|
||||
17
AntSK.Domain/Domain/Interface/IKernelService.cs
Normal file
17
AntSK.Domain/Domain/Interface/IKernelService.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using AntSK.Domain.Repositories;
|
||||
using Microsoft.SemanticKernel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Domain.Interface
|
||||
{
|
||||
public interface IKernelService
|
||||
{
|
||||
Kernel GetKernel(string modelId = null, string apiKey = null);
|
||||
void ImportFunctionsByApp(Apps app, Kernel _kernel);
|
||||
Task<string> HistorySummarize(Kernel _kernel, string questions, string history);
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,71 @@ using AntSK.Domain.Domain.Interface;
|
||||
using Microsoft.KernelMemory;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Domain.Domain.Dto;
|
||||
using AntSK.Domain.Options;
|
||||
using Microsoft.KernelMemory.ContentStorage.DevTools;
|
||||
using Microsoft.KernelMemory.FileSystem.DevTools;
|
||||
using Microsoft.KernelMemory.Postgres;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.KernelMemory.Configuration;
|
||||
|
||||
namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
[ServiceDescription(typeof(IKMService), ServiceLifetime.Scoped)]
|
||||
public class KMService(MemoryServerless _memory) : IKMService
|
||||
public class KMService(
|
||||
IOptions<PostgresConfig> postgresOptions
|
||||
) : IKMService
|
||||
{
|
||||
public MemoryServerless GetMemory(SearchClientConfig searchClientConfig=null, TextPartitioningOptions textPartitioningOptions=null)
|
||||
{
|
||||
var handler = new OpenAIHttpClientHandler();
|
||||
var httpClient = new HttpClient(handler);
|
||||
if (searchClientConfig.IsNull())
|
||||
{
|
||||
searchClientConfig = new SearchClientConfig
|
||||
{
|
||||
MaxAskPromptSize = 2048,
|
||||
MaxMatchesCount = 3,
|
||||
AnswerTokens = 1000,
|
||||
EmptyAnswer = "知识库未搜索到相关内容"
|
||||
};
|
||||
}
|
||||
|
||||
if (textPartitioningOptions.IsNull())
|
||||
{
|
||||
textPartitioningOptions = new TextPartitioningOptions
|
||||
{
|
||||
MaxTokensPerLine = 99,
|
||||
MaxTokensPerParagraph = 299,
|
||||
OverlappingTokens = 47
|
||||
};
|
||||
}
|
||||
|
||||
var memory = new KernelMemoryBuilder()
|
||||
.WithPostgresMemoryDb(postgresOptions.Value)
|
||||
.WithSimpleFileStorage(new SimpleFileStorageConfig { StorageType = FileSystemTypes.Volatile, Directory = "_files" })
|
||||
.WithSearchClientConfig(searchClientConfig)
|
||||
//如果用本地模型需要设置token小一点。
|
||||
.WithCustomTextPartitioningOptions(textPartitioningOptions)
|
||||
.WithOpenAITextGeneration(new OpenAIConfig()
|
||||
{
|
||||
APIKey = OpenAIOption.Key,
|
||||
TextModel = OpenAIOption.Model
|
||||
|
||||
}, null, httpClient)
|
||||
.WithOpenAITextEmbeddingGeneration(new OpenAIConfig()
|
||||
{
|
||||
APIKey = OpenAIOption.Key,
|
||||
EmbeddingModel = OpenAIOption.EmbeddingModel
|
||||
|
||||
}, null, false, httpClient)
|
||||
.Build<MemoryServerless>();
|
||||
return memory;
|
||||
}
|
||||
|
||||
public async Task<List<KMFile>> GetDocumentByFileID(string fileid)
|
||||
{
|
||||
var _memory = GetMemory();
|
||||
var memories = await _memory.ListIndexesAsync();
|
||||
var memoryDbs = _memory.Orchestrator.GetMemoryDbs();
|
||||
List<KMFile> docTextList = new List<KMFile>();
|
||||
@@ -19,23 +76,19 @@ namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
foreach (var memoryDb in memoryDbs)
|
||||
{
|
||||
var list = memoryDb.GetListAsync(memoryIndex.Name, null, 100, true);
|
||||
|
||||
await foreach (var item in list)
|
||||
var items = await memoryDb.GetListAsync(memoryIndex.Name, new List<MemoryFilter>() { new MemoryFilter().ByDocument(fileid) }, 100, true).ToListAsync();
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Id.Contains(fileid))
|
||||
KMFile file = new KMFile()
|
||||
{
|
||||
|
||||
KMFile file = new KMFile()
|
||||
{
|
||||
Text = item.Payload.FirstOrDefault(p => p.Key == "text").Value.ConvertToString(),
|
||||
Url= item.Payload.FirstOrDefault(p => p.Key == "url").Value.ConvertToString(),
|
||||
LastUpdate= item.Payload.FirstOrDefault(p => p.Key == "last_update").Value.ConvertToString(),
|
||||
Schema = item.Payload.FirstOrDefault(p => p.Key == "schema").Value.ConvertToString(),
|
||||
File = item.Payload.FirstOrDefault(p => p.Key == "file").Value.ConvertToString(),
|
||||
};
|
||||
docTextList.Add(file);
|
||||
}
|
||||
Text = item.Payload.FirstOrDefault(p => p.Key == "text").Value.ConvertToString(),
|
||||
Url = item.Payload.FirstOrDefault(p => p.Key == "url").Value.ConvertToString(),
|
||||
LastUpdate = item.Payload.FirstOrDefault(p => p.Key == "last_update").Value.ConvertToString(),
|
||||
Schema = item.Payload.FirstOrDefault(p => p.Key == "schema").Value.ConvertToString(),
|
||||
File = item.Payload.FirstOrDefault(p => p.Key == "file").Value.ConvertToString(),
|
||||
};
|
||||
docTextList.Add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
157
AntSK.Domain/Domain/Service/KernelService.cs
Normal file
157
AntSK.Domain/Domain/Service/KernelService.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Plugins.Core;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Domain.Service
|
||||
{
|
||||
[ServiceDescription(typeof(IKernelService), ServiceLifetime.Scoped)]
|
||||
public class KernelService(
|
||||
IApis_Repositories _apis_Repositories
|
||||
) : IKernelService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取kernel实例,依赖注入不好按每个用户去Import不同的插件,所以每次new一个新的kernel
|
||||
/// </summary>
|
||||
/// <param name="modelId"></param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <returns></returns>
|
||||
public Kernel GetKernel(string modelId=null,string apiKey=null)
|
||||
{
|
||||
var handler = new OpenAIHttpClientHandler();
|
||||
var httpClient = new HttpClient(handler);
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: modelId!=null? modelId : OpenAIOption.Model,
|
||||
apiKey: apiKey!=null? apiKey: OpenAIOption.Key,
|
||||
httpClient: httpClient)
|
||||
.Build();
|
||||
RegisterPluginsWithKernel(kernel);
|
||||
return kernel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据app配置的插件,导入插件
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="_kernel"></param>
|
||||
public void ImportFunctionsByApp(Apps app, Kernel _kernel)
|
||||
{
|
||||
//开启自动插件调用
|
||||
var apiIdList = app.ApiFunctionList.Split(",");
|
||||
var apiList = _apis_Repositories.GetList(p => apiIdList.Contains(p.Id));
|
||||
List<KernelFunction> functions = new List<KernelFunction>();
|
||||
var plugin = _kernel.Plugins.FirstOrDefault(p => p.Name == "ApiFunctions");
|
||||
{
|
||||
foreach (var api in apiList)
|
||||
{
|
||||
switch (api.Method)
|
||||
{
|
||||
case HttpMethodType.Get:
|
||||
functions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine(msg);
|
||||
RestClient client = new RestClient();
|
||||
RestRequest request = new RestRequest(api.Url, Method.Get);
|
||||
foreach (var header in api.Header.Split("\n"))
|
||||
{
|
||||
var headerArray = header.Split(":");
|
||||
if (headerArray.Length == 2)
|
||||
{
|
||||
request.AddHeader(headerArray[0], headerArray[1]);
|
||||
}
|
||||
}
|
||||
//这里应该还要处理一次参数提取,等后面再迭代
|
||||
foreach (var query in api.Query.Split("\n"))
|
||||
{
|
||||
var queryArray = query.Split("=");
|
||||
if (queryArray.Length == 2)
|
||||
{
|
||||
request.AddQueryParameter(queryArray[0], queryArray[1]);
|
||||
}
|
||||
}
|
||||
var result = client.Execute(request);
|
||||
return result.Content;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return "调用失败:" + ex.Message;
|
||||
}
|
||||
}, api.Name, $"{api.Describe}"));
|
||||
break;
|
||||
case HttpMethodType.Post:
|
||||
functions.Add(_kernel.CreateFunctionFromMethod((string msg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine(msg);
|
||||
RestClient client = new RestClient();
|
||||
RestRequest request = new RestRequest(api.Url, Method.Post);
|
||||
foreach (var header in api.Header.Split("\n"))
|
||||
{
|
||||
var headerArray = header.Split(":");
|
||||
if (headerArray.Length == 2)
|
||||
{
|
||||
request.AddHeader(headerArray[0], headerArray[1]);
|
||||
}
|
||||
}
|
||||
//这里应该还要处理一次参数提取,等后面再迭代
|
||||
request.AddJsonBody(api.JsonBody);
|
||||
var result = client.Execute(request);
|
||||
return result.Content;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return "调用失败:" + ex.Message;
|
||||
}
|
||||
}, api.Name, $"{api.Describe}"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
_kernel.ImportPluginFromFunctions("ApiFunctions", functions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册默认插件
|
||||
/// </summary>
|
||||
/// <param name="kernel"></param>
|
||||
void RegisterPluginsWithKernel(Kernel kernel)
|
||||
{
|
||||
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
|
||||
kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
|
||||
kernel.ImportPluginFromPromptDirectory(Path.Combine(RepoFiles.SamplePluginsPath(), "KMSPlugin"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 会话总结
|
||||
/// </summary>
|
||||
/// <param name="_kernel"></param>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="history"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> HistorySummarize(Kernel _kernel,string questions, string history)
|
||||
{
|
||||
KernelFunction sunFun = _kernel.Plugins.GetFunction("ConversationSummaryPlugin", "SummarizeConversation");
|
||||
var summary = await _kernel.InvokeAsync(sunFun, new() { ["input"] = $"内容是:{history.ToString()} {Environment.NewLine} 请注意用中文总结" });
|
||||
string his = summary.GetValue<string>();
|
||||
var msg = $"history:{history.ToString()}{Environment.NewLine} user:{questions}"; ;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
AntSK.Domain/Model/Enum/AIModelType.cs
Normal file
14
AntSK.Domain/Model/Enum/AIModelType.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Model.Enum
|
||||
{
|
||||
public enum AIModelType
|
||||
{
|
||||
Chat = 1,
|
||||
Embedding = 2,
|
||||
}
|
||||
}
|
||||
15
AntSK.Domain/Model/Enum/HttpMethodType.cs
Normal file
15
AntSK.Domain/Model/Enum/HttpMethodType.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.Model
|
||||
{
|
||||
public enum HttpMethodType
|
||||
{
|
||||
Get = 1,
|
||||
Post = 2,
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,13 @@ namespace AntSK.Domain.Model
|
||||
public class MessageInfo
|
||||
{
|
||||
public string ID { get; set; } = "";
|
||||
public string Questions { get; set; } = "";
|
||||
public string Answers { get; set; } = "";
|
||||
public string Context { get; set; } = "";
|
||||
public string HtmlAnswers { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 发送是true 接收是false
|
||||
/// </summary>
|
||||
public bool IsSend { get; set; } = false;
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
}
|
||||
|
||||
15
AntSK.Domain/Options/LLamaSharpOption.cs
Normal file
15
AntSK.Domain/Options/LLamaSharpOption.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.Options
|
||||
{
|
||||
public class LLamaSharpOption
|
||||
{
|
||||
public static string Chat { get; set; }
|
||||
|
||||
public static string Embedding { get; set; }
|
||||
}
|
||||
}
|
||||
15
AntSK.Domain/Options/LoginOption.cs
Normal file
15
AntSK.Domain/Options/LoginOption.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.Options
|
||||
{
|
||||
public class LoginOption
|
||||
{
|
||||
public static string User { get; set; }
|
||||
|
||||
public static string Password { get; set; }
|
||||
}
|
||||
}
|
||||
67
AntSK.Domain/Repositories/AI/Api/Apis.cs
Normal file
67
AntSK.Domain/Repositories/AI/Api/Apis.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using AntSK.Domain.Model;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
[SugarTable("Apis")]
|
||||
public partial class Apis
|
||||
{
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 接口名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 接口描述
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Describe { get; set; }
|
||||
/// <summary>
|
||||
/// 接口地址
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Url { get; set; }
|
||||
/// <summary>
|
||||
/// 请求方法
|
||||
/// </summary>
|
||||
[Required]
|
||||
public HttpMethodType Method { get; set; }
|
||||
|
||||
[SugarColumn(ColumnDataType = "varchar(2000)")]
|
||||
public string? Header { get; set; }
|
||||
/// <summary>
|
||||
/// QueryString参数
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "varchar(2000)")]
|
||||
public string? Query { get; set; }
|
||||
/// <summary>
|
||||
/// jsonBody 实体
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "varchar(8000)")]
|
||||
public string? JsonBody { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入参提示词
|
||||
/// </summary>
|
||||
[Required]
|
||||
[SugarColumn(ColumnDataType = "varchar(2000)")]
|
||||
public string InputPrompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回提示词
|
||||
/// </summary>
|
||||
[Required]
|
||||
[SugarColumn(ColumnDataType = "varchar(2000)")]
|
||||
public string OutputPrompt { get; set; }
|
||||
}
|
||||
}
|
||||
16
AntSK.Domain/Repositories/AI/Api/Apis_Repositories.cs
Normal file
16
AntSK.Domain/Repositories/AI/Api/Apis_Repositories.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
[ServiceDescription(typeof(IApis_Repositories), ServiceLifetime.Scoped)]
|
||||
public class Apis_Repositories : Repository<Apis>, IApis_Repositories
|
||||
{
|
||||
}
|
||||
}
|
||||
13
AntSK.Domain/Repositories/AI/Api/IApis_Repositories.cs
Normal file
13
AntSK.Domain/Repositories/AI/Api/IApis_Repositories.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
public interface IApis_Repositories : IRepository<Apis>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -25,19 +25,41 @@ namespace AntSK.Domain.Repositories
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Describe { get; set; }
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 温度
|
||||
/// </summary>
|
||||
public double Temperature { get; set; }=70f;
|
||||
|
||||
/// <summary>
|
||||
/// 提示词
|
||||
/// </summary>
|
||||
public string? Prompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件列表
|
||||
/// </summary>
|
||||
public string? ApiFunctionList { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 知识库ID列表
|
||||
/// </summary>
|
||||
public string? KmsIdList { get; set; }
|
||||
/// <summary>
|
||||
/// API调用秘钥
|
||||
/// </summary>
|
||||
public string? SecretKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,5 +28,22 @@ namespace AntSK.Domain.Repositories
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Describe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每个段落的最大标记数。
|
||||
/// </summary>
|
||||
public int MaxTokensPerParagraph { get; set; } = 1000;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 每行,也就是每句话的最大标记数
|
||||
/// </summary>
|
||||
public int MaxTokensPerLine { get; set; } = 300;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 段落之间重叠标记的数量。
|
||||
/// </summary>
|
||||
public int OverlappingTokens { get; set; } = 100;
|
||||
}
|
||||
}
|
||||
|
||||
40
AntSK.Domain/Repositories/Setting/AIModel/AIModels.cs
Normal file
40
AntSK.Domain/Repositories/Setting/AIModel/AIModels.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using AntSK.Domain.Model.Enum;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
[SugarTable("AIModels")]
|
||||
public partial class AIModels
|
||||
{
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public string Id { get; set; }
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
[Required]
|
||||
public AIModelType AIModelType { get; set; }
|
||||
/// <summary>
|
||||
/// 模型地址
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string EndPoint { get; set; }
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string ModelName { get; set; }
|
||||
/// <summary>
|
||||
/// 模型秘钥
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string ModelKey { get; set; }
|
||||
[Required]
|
||||
public string ModelDescription { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
[ServiceDescription(typeof(IAIModels_Repositories), ServiceLifetime.Scoped)]
|
||||
public class AIModels_Repositories : Repository<AIModels>, IAIModels_Repositories
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
public interface IAIModels_Repositories : IRepository<AIModels>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
public interface IUsers_Repositories : IRepository<Users>
|
||||
{
|
||||
}
|
||||
}
|
||||
47
AntSK.Domain/Repositories/Setting/User/Users.cs
Normal file
47
AntSK.Domain/Repositories/Setting/User/Users.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
[SugarTable("Users")]
|
||||
public partial class Users
|
||||
{
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工号,用于登陆
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string No { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Describe { get; set; }
|
||||
/// <summary>
|
||||
/// 菜单权限
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string MenuRole { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
16
AntSK.Domain/Repositories/Setting/User/Users_Repositories.cs
Normal file
16
AntSK.Domain/Repositories/Setting/User/Users_Repositories.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
using AntSK.Domain.Repositories.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
|
||||
namespace AntSK.Domain.Repositories
|
||||
{
|
||||
[ServiceDescription(typeof(IUsers_Repositories), ServiceLifetime.Scoped)]
|
||||
public class Users_Repositories : Repository<Users>, IUsers_Repositories
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Buffers.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AntSK.Utils
|
||||
namespace AntSK.Domain.Utils
|
||||
{
|
||||
public class LongToDateTimeConverter : JsonConverter<DateTime>
|
||||
{
|
||||
@@ -13,13 +13,25 @@ namespace AntSK.Domain.Utils
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
UriBuilder uriBuilder;
|
||||
Regex regex = new Regex(@"(https?)://([^/]+)/(.*)");
|
||||
Regex regex = new Regex(@"(https?)://([^/:]+)(:\d+)?/(.*)");
|
||||
Match match = regex.Match(OpenAIOption.EndPoint);
|
||||
string xieyi = match.Groups[1].Value;
|
||||
string host = match.Groups[2].Value;
|
||||
string route = match.Groups[3].Value;
|
||||
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" && request.Content != null)
|
||||
{
|
||||
string requestBody = await request.Content.ReadAsStringAsync();
|
||||
//便于调试查看请求prompt
|
||||
Console.WriteLine(requestBody);
|
||||
}
|
||||
if (match.Success)
|
||||
{
|
||||
string xieyi = match.Groups[1].Value;
|
||||
string host = match.Groups[2].Value;
|
||||
string port = match.Groups[3].Value; // 可选的端口号
|
||||
string route = match.Groups[4].Value;
|
||||
// 如果port不为空,它将包含冒号,所以你可能需要去除它
|
||||
port = string.IsNullOrEmpty(port) ? port : port.Substring(1);
|
||||
// 拼接host和端口号
|
||||
var hostnew = string.IsNullOrEmpty(port) ? host : $"{host}:{port}";
|
||||
|
||||
switch (request.RequestUri.LocalPath)
|
||||
{
|
||||
case "/v1/chat/completions":
|
||||
@@ -27,10 +39,15 @@ namespace AntSK.Domain.Utils
|
||||
uriBuilder = new UriBuilder(request.RequestUri)
|
||||
{
|
||||
// 这里是你要修改的 URL
|
||||
Scheme = $"{xieyi}://{host}/",
|
||||
Host = host,
|
||||
Scheme = $"{xieyi}://{hostnew}/",
|
||||
Host = host,
|
||||
Path = route + "v1/chat/completions",
|
||||
};
|
||||
if (port.ConvertToInt32() != 0)
|
||||
{
|
||||
uriBuilder.Port = port.ConvertToInt32();
|
||||
}
|
||||
|
||||
request.RequestUri = uriBuilder.Uri;
|
||||
|
||||
break;
|
||||
@@ -42,10 +59,15 @@ namespace AntSK.Domain.Utils
|
||||
Host = host,
|
||||
Path = route + "v1/embeddings",
|
||||
};
|
||||
if (port.ConvertToInt32() != 0)
|
||||
{
|
||||
uriBuilder.Port = port.ConvertToInt32();
|
||||
}
|
||||
request.RequestUri = uriBuilder.Uri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 接着,调用基类的 SendAsync 方法将你的修改后的请求发出去
|
||||
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
|
||||
|
||||
|
||||
19
AntSK.Domain/Utils/PasswordUtil.cs
Normal file
19
AntSK.Domain/Utils/PasswordUtil.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using BCrypt.Net;
|
||||
|
||||
namespace AntSK.Domain.Utils
|
||||
{
|
||||
public class PasswordUtil
|
||||
{
|
||||
public static string HashPassword(string password)
|
||||
{
|
||||
// 默认的工作因子是10,可以根据你的需求调整以增加散列的复杂度
|
||||
return BCrypt.Net.BCrypt.HashPassword(password);
|
||||
}
|
||||
|
||||
// 验证密码是否匹配散列值
|
||||
public static bool VerifyPassword(string password, string hashedPassword)
|
||||
{
|
||||
return BCrypt.Net.BCrypt.Verify(password, hashedPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
AntSK.Domain/Utils/RepoFiles.cs
Normal file
59
AntSK.Domain/Utils/RepoFiles.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Utils
|
||||
{
|
||||
public static class RepoFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Scan the local folders from the repo, looking for "samples/plugins" folder.
|
||||
/// </summary>
|
||||
/// <returns>The full path to samples/plugins</returns>
|
||||
public static string SamplePluginsPath()
|
||||
{
|
||||
string Parent = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string Folder = "plugins";
|
||||
|
||||
bool SearchPath(string pathToFind, out string result, int maxAttempts = 10)
|
||||
{
|
||||
var currDir = Path.GetFullPath(Assembly.GetExecutingAssembly().Location);
|
||||
bool found;
|
||||
do
|
||||
{
|
||||
result = Path.Join(currDir, pathToFind);
|
||||
found = Directory.Exists(result);
|
||||
currDir = Path.GetFullPath(Path.Combine(currDir, ".."));
|
||||
} while (maxAttempts-- > 0 && !found);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
if (!SearchPath(Parent + Path.DirectorySeparatorChar + Folder, out string path)
|
||||
&& !SearchPath(Folder, out path))
|
||||
{
|
||||
throw new YourAppException("Plugins directory not found. The app needs the plugins from the repo to work.");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public class YourAppException : Exception
|
||||
{
|
||||
public YourAppException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public YourAppException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public YourAppException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntSK.Domain", "AntSK.Domai
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docker", "Docker", "{9F2E193A-5F9D-4C82-B591-CB133EEB59F0}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
docker-compose.yml = docker-compose.yml
|
||||
Dockerfile = Dockerfile
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DocumentationFile>AntSK.xml</DocumentationFile>
|
||||
<NoWarn>CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AntDesign.Charts" Version="0.5.1" />
|
||||
<PackageReference Include="AntDesign.ProLayout" Version="0.17.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
@@ -19,7 +22,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\Setting\" />
|
||||
<None Update="plugins\KMSPlugin\Ask\skprompt.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
115
AntSK/AntSK.xml
115
AntSK/AntSK.xml
@@ -16,6 +16,85 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.LLamaSharpController.chat(AntSK.Models.OpenAIModel)">
|
||||
<summary>
|
||||
本地会话接口
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.LLamaSharpController.embedding(AntSK.Models.OpenAIEmbeddingModel)">
|
||||
<summary>
|
||||
本地嵌入接口
|
||||
</summary>
|
||||
<param name="model"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Controllers.OpenController">
|
||||
<summary>
|
||||
对外接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.OpenController.#ctor(AntSK.Services.OpenApi.IOpenApiService)">
|
||||
<summary>
|
||||
对外接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.OpenController.chat(AntSK.Models.OpenAIModel)">
|
||||
<summary>
|
||||
对话接口
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.Chat.SendKms(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.Chat.SendChat(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送普通对话
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.Chat.HistorySummarize(System.String)">
|
||||
<summary>
|
||||
历史会话的会话总结
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.OpenChat.SendKms(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.OpenChat.SendChat(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送普通对话
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.OpenChat.HistorySummarize(System.String)">
|
||||
<summary>
|
||||
历史会话的会话总结
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Pages.KmsPage.KmsDetail.UrlModel">
|
||||
<summary>
|
||||
根据文档ID获取文档
|
||||
@@ -23,5 +102,41 @@
|
||||
<param name="fileid"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Services.LLamaSharp.LLamaChatService">
|
||||
<summary>
|
||||
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:AntSK.Services.LLamaSharp.LLamaEmbeddingService">
|
||||
<summary>
|
||||
本地Embedding
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendChat(System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送普通对话
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendKms(System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.HistorySummarize(AntSK.Models.OpenAIModel)">
|
||||
<summary>
|
||||
历史会话的会话总结
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<CascadingValue Value="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
|
||||
</CascadingValue>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(BasicLayout)">
|
||||
<p>Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<AntContainer />
|
||||
@inject NavigationManager NavigationManager
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<CascadingValue Value="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
|
||||
</CascadingValue>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(BasicLayout)">
|
||||
<AntSK.Pages.Exception._404/>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<AntContainer />
|
||||
</CascadingAuthenticationState>
|
||||
|
||||
@code {
|
||||
private RenderFragment RedirectToLogin => builder =>
|
||||
{
|
||||
var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
|
||||
if (string.IsNullOrWhiteSpace(returnUrl))
|
||||
{
|
||||
NavigationManager.NavigateTo("user/login");
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo($"user/login?returnUrl={Uri.EscapeDataString(returnUrl)}", true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
@namespace AntSK.Components
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@inherits AntDomComponentBase
|
||||
|
||||
<Space Class="@ClassMapper.Class" Size="@("24")">
|
||||
<Space Class="@ClassMapper.Class" Size="@("26")">
|
||||
<SpaceItem>
|
||||
<AvatarDropdown Name="@_currentUser.Name"
|
||||
<AvatarDropdown Name="@context.Identity.Name"
|
||||
Avatar="@_currentUser.Avatar"
|
||||
MenuItems="@AvatarMenuItems"
|
||||
OnItemSelected="HandleSelectUser" />
|
||||
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@@ -6,6 +6,10 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Security.Claims;
|
||||
using AntSK.Services.Auth;
|
||||
using AntSK.Domain.Options;
|
||||
|
||||
namespace AntSK.Components
|
||||
{
|
||||
@@ -38,7 +42,6 @@ namespace AntSK.Components
|
||||
|
||||
public AvatarMenuItem[] AvatarMenuItems { get; set; } = new AvatarMenuItem[]
|
||||
{
|
||||
new() { Key = "center", IconType = "user", Option = "个人中心"},
|
||||
new() { Key = "setting", IconType = "setting", Option = "个人设置"},
|
||||
new() { IsDivider = true },
|
||||
new() { Key = "logout", IconType = "logout", Option = "退出登录"}
|
||||
@@ -50,6 +53,11 @@ namespace AntSK.Components
|
||||
[Inject] protected IProjectService ProjectService { get; set; }
|
||||
[Inject] protected MessageService MessageService { get; set; }
|
||||
|
||||
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
|
||||
[Inject] protected MessageService? Message { get; set; }
|
||||
|
||||
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
@@ -73,11 +81,15 @@ namespace AntSK.Components
|
||||
{
|
||||
switch (item.Key)
|
||||
{
|
||||
case "center":
|
||||
NavigationManager.NavigateTo("/account/center");
|
||||
break;
|
||||
case "setting":
|
||||
NavigationManager.NavigateTo("/account/settings");
|
||||
if (context.Identity.Name != LoginOption.User)
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/user/info/" + context.Identity.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Message.Info("管理员无需设置", 2);
|
||||
}
|
||||
break;
|
||||
case "logout":
|
||||
NavigationManager.NavigateTo("/user/login");
|
||||
|
||||
47
AntSK/Controllers/LLamaSharpController.cs
Normal file
47
AntSK/Controllers/LLamaSharpController.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using AntSK.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Services.LLamaSharp;
|
||||
|
||||
namespace AntSK.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class LLamaSharpController(ILLamaSharpService _lLamaSharpService) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地会话接口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("llama/v1/chat/completions")]
|
||||
public async Task chat(OpenAIModel model)
|
||||
{
|
||||
Console.WriteLine("开始:llama/v1/chat/completions");
|
||||
if (model.stream)
|
||||
{
|
||||
await _lLamaSharpService.ChatStream(model, HttpContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _lLamaSharpService.Chat(model, HttpContext);
|
||||
}
|
||||
Console.WriteLine("结束:llama/v1/chat/completions");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地嵌入接口
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("llama/v1/embeddings")]
|
||||
public async Task embedding(OpenAIEmbeddingModel model)
|
||||
{
|
||||
Console.WriteLine("开始:llama/v1/embeddings");
|
||||
await _lLamaSharpService.Embedding(model,HttpContext);
|
||||
Console.WriteLine("结束:llama/v1/embeddings");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
31
AntSK/Controllers/OpenController.cs
Normal file
31
AntSK/Controllers/OpenController.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Models;
|
||||
using AntSK.Models.OpenAPI;
|
||||
using AntSK.Services.OpenApi;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AntSK.Controllers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 对外接口
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
public class OpenController(IOpenApiService _openApiService) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 对话接口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("api/v1/chat/completions")]
|
||||
public async Task chat(OpenAIModel model)
|
||||
{
|
||||
string sk = HttpContext.Request.Headers["Authorization"].ConvertToString();
|
||||
await _openApiService.Chat(model,sk, HttpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
@namespace AntSK
|
||||
@using System.Security.Claims
|
||||
@using AntSK.Services.Auth
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using AntSK.Domain.Options
|
||||
@using AntSK.Domain.Repositories
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<AntDesign.ProLayout.BasicLayout
|
||||
@@ -21,11 +26,28 @@
|
||||
private MenuDataItem[] _menuData = { };
|
||||
|
||||
[Inject] public HttpClient HttpClient { get; set; }
|
||||
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
|
||||
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
|
||||
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_menuData = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
|
||||
//菜单权限控制
|
||||
var menuList = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
|
||||
if ((bool)context?.Identity.IsAuthenticated)
|
||||
{
|
||||
if (context.Identity.Name == LoginOption.User)
|
||||
{
|
||||
_menuData = menuList;
|
||||
}
|
||||
else
|
||||
{
|
||||
var userMenuList = _users_Repositories.GetFirst(p => p.No == context.Identity.Name).MenuRole.Split(",").ToList();
|
||||
//非管理员用户不允许操作系统设置
|
||||
_menuData = menuList.Where(p => p.Key != "setting" && userMenuList.Contains(p.Key)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
8
AntSK/Layouts/OpenLayout.razor
Normal file
8
AntSK/Layouts/OpenLayout.razor
Normal file
@@ -0,0 +1,8 @@
|
||||
@namespace AntSK
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@Body
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -11,14 +11,14 @@
|
||||
<div class="header">
|
||||
<a>
|
||||
<img alt="logo" class="logo" src="assets/logo.svg" />
|
||||
<span class="title">Ant Design</span>
|
||||
<span class="title">AntSK</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="desc">Ant Design Blazor</div>
|
||||
<div class="desc"> </div>
|
||||
</div>
|
||||
@Body
|
||||
</div>
|
||||
<FooterView Copyright="2021 Ant Design Blazor" Links="Links"></FooterView>
|
||||
<FooterView Copyright="许泽宇的技术分享" Links="Links"></FooterView>
|
||||
</div>
|
||||
|
||||
@code
|
||||
|
||||
28
AntSK/Models/OpenAPI/OpenAIModel.cs
Normal file
28
AntSK/Models/OpenAPI/OpenAIModel.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Models
|
||||
{
|
||||
public class OpenAIModel
|
||||
{
|
||||
public bool stream { get; set; } = false;
|
||||
public List<OpenAIMessage> messages { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIMessage
|
||||
{
|
||||
public string role { get; set; }
|
||||
|
||||
public string content { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIEmbeddingModel
|
||||
{
|
||||
public List<string> input { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
67
AntSK/Models/OpenAPI/OpenAIResult.cs
Normal file
67
AntSK/Models/OpenAPI/OpenAIResult.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AntSK.Models.OpenAPI
|
||||
{
|
||||
public class OpenAIResult
|
||||
{
|
||||
public string id { get; set; } = Guid.NewGuid().ToString();
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "chat.completion";
|
||||
public List<ChoicesModel> choices { get; set; }
|
||||
public long created { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class ChoicesModel
|
||||
{
|
||||
public string finish_reason { get; set; } = "stop";
|
||||
public int index { get; set; } = 0;
|
||||
|
||||
public OpenAIMessage message { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIEmbeddingResult
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "list";
|
||||
public string model { get; set; } = "ada";
|
||||
|
||||
public UsageModel usage { get; set; } = new UsageModel();
|
||||
|
||||
public List<DataModel> data { get; set; } = new List<DataModel>() { new DataModel() };
|
||||
}
|
||||
|
||||
public class UsageModel
|
||||
{
|
||||
public long prompt_tokens { get; set; } = 0;
|
||||
|
||||
public long total_tokens { get; set; } = 0;
|
||||
}
|
||||
|
||||
public class DataModel
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "embedding";
|
||||
public int index { get; set; } = 0;
|
||||
|
||||
public List<float> embedding { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class OpenAIStreamResult
|
||||
{
|
||||
public string id { get; set; } = Guid.NewGuid().ToString();
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "chat.completion";
|
||||
public List<StreamChoicesModel> choices { get; set; }
|
||||
public long created { get; set; }
|
||||
}
|
||||
public class StreamChoicesModel
|
||||
{
|
||||
public int index { get; set; } = 0;
|
||||
|
||||
public OpenAIMessage delta { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
43
AntSK/Models/Template/LayoutModel.cs
Normal file
43
AntSK/Models/Template/LayoutModel.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using AntDesign;
|
||||
|
||||
namespace AntSK.Models
|
||||
{
|
||||
public class LayoutModel
|
||||
{
|
||||
public static FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
|
||||
public static FormItemLayout _submitFormLayout = new FormItemLayout
|
||||
{
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
|
||||
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
|
||||
}
|
||||
};
|
||||
|
||||
public static ListGridType _listGridType = new ListGridType
|
||||
{
|
||||
Gutter = 16,
|
||||
Xs = 1,
|
||||
Sm = 2,
|
||||
Md = 3,
|
||||
Lg = 3,
|
||||
Xl = 4,
|
||||
Xxl = 4
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using AntSK.Utils;
|
||||
using AntSK.Domain.Utils;
|
||||
|
||||
namespace AntSK.Models
|
||||
{
|
||||
76
AntSK/Pages/ApiPage/AddApi.razor
Normal file
76
AntSK/Pages/ApiPage/AddApi.razor
Normal file
@@ -0,0 +1,76 @@
|
||||
@namespace AntSK.Pages.ApiPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@using AntSK.Domain.Model
|
||||
@page "/plugins/api/add"
|
||||
@page "/plugins/api/add/{ApiId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="新增API">
|
||||
<ChildContent>
|
||||
<Card>
|
||||
<Form Model="@_apiModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="注意" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<h1>API插件需要使用FunctionCall能力,建议使用GPT4,国产模型可能不太行</h1>
|
||||
</FormItem>
|
||||
<FormItem Label="Api名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入Api名称" @bind-Value="@context.Name" />
|
||||
</FormItem>
|
||||
<FormItem Label="Api描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入Api描述" @bind-Value="@context.Describe" />
|
||||
</FormItem>
|
||||
<FormItem Label="Url地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入Url地址" @bind-Value="@context.Url" />
|
||||
</FormItem>
|
||||
<FormItem Label="HttpHeader" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<TextArea Placeholder="一个一行,以冒号分割。例如:Content-Type:application/json" @bind-Value="@context.Header" MinRows="3" />
|
||||
</FormItem>
|
||||
<FormItem Label="Method类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.Method">
|
||||
<Radio RadioButton Value="@(HttpMethodType.Get)">Get</Radio>
|
||||
<Radio RadioButton Value="@(HttpMethodType.Post)">Post</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
|
||||
@if (context.Method == HttpMethodType.Get)
|
||||
{
|
||||
<FormItem Label="Query" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<TextArea Placeholder="一个一行,以等号分割。例如:name=a" @bind-Value="@context.Query" MinRows="5" />
|
||||
</FormItem>
|
||||
}
|
||||
else if (context.Method == HttpMethodType.Post)
|
||||
{
|
||||
<FormItem Label="JsonBody" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<TextArea Placeholder="请输入JsonBody" @bind-Value="@context.JsonBody" MinRows="5" />
|
||||
</FormItem>
|
||||
}
|
||||
|
||||
<FormItem Label="入参提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<TextArea Placeholder="请输入入参提示词" @bind-Value="@context.InputPrompt" MinRows="5" />
|
||||
</FormItem>
|
||||
<FormItem Label="返回提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<TextArea Placeholder="请输入返回提示词" @bind-Value="@context.OutputPrompt" MinRows="5" />
|
||||
</FormItem>
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" HtmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
<Button OnClick="Back">
|
||||
返回
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
|
||||
|
||||
<style>
|
||||
.avatar-uploader > .ant-upload {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
</style>
|
||||
72
AntSK/Pages/ApiPage/AddApi.razor.cs
Normal file
72
AntSK/Pages/ApiPage/AddApi.razor.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using AntDesign;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AntSK.Pages.ApiPage
|
||||
{
|
||||
public partial class AddApi
|
||||
{
|
||||
[Parameter]
|
||||
public string ApiId { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected IApis_Repositories _apis_Repositories { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
|
||||
private Apis _apiModel = new Apis() ;
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (!string.IsNullOrEmpty(ApiId))
|
||||
{
|
||||
//查看
|
||||
_apiModel = _apis_Repositories.GetFirst(p => p.Id == ApiId);
|
||||
}
|
||||
}
|
||||
private void HandleSubmit()
|
||||
{
|
||||
if (string.IsNullOrEmpty(ApiId))
|
||||
{
|
||||
//新增
|
||||
_apiModel.Id = Guid.NewGuid().ToString();
|
||||
|
||||
if (_apis_Repositories.IsAny(p => p.Name == _apiModel.Name))
|
||||
{
|
||||
_ = Message.Error("名称已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
string pattern = @"^[A-Za-z]\w*$"; // 正则表达式模式
|
||||
if (!Regex.IsMatch(_apiModel.Name, pattern))
|
||||
{
|
||||
_ = Message.Error("API名称只能是字母、数字、下划线组成,且不能以数字开头!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
_apis_Repositories.Insert(_apiModel);
|
||||
}
|
||||
else {
|
||||
//修改
|
||||
|
||||
_apis_Repositories.Update(_apiModel);
|
||||
}
|
||||
|
||||
Back();
|
||||
}
|
||||
|
||||
|
||||
private void Back() {
|
||||
NavigationManager.NavigateTo("/plugins/apilist");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
73
AntSK/Pages/ApiPage/ApiList.razor
Normal file
73
AntSK/Pages/ApiPage/ApiList.razor
Normal file
@@ -0,0 +1,73 @@
|
||||
@namespace AntSK.Pages.ApiPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Domain.Model
|
||||
@page "/plugins/apilist"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="API列表">
|
||||
<Content>
|
||||
<div style="text-align: center;">
|
||||
<Search Placeholder="输入回车"
|
||||
EnterButton="@("搜索")"
|
||||
Size="large"
|
||||
Style="max-width: 522px; width: 100%;"
|
||||
OnSearch="Search" />
|
||||
</div>
|
||||
</Content>
|
||||
<ChildContent>
|
||||
<div class="cardList">
|
||||
<AntList TItem="Apis"
|
||||
DataSource="_data"
|
||||
ItemLayout="ListItemLayout.Horizontal"
|
||||
Grid="LayoutModel._listGridType">
|
||||
<ListItem NoFlex>
|
||||
@if (string.IsNullOrEmpty(context.Id))
|
||||
{
|
||||
<Button Type="dashed" class="newButton" @onclick="NavigateToAddApp">
|
||||
<Icon Type="plus" Theme="outline" /> 创建API
|
||||
</Button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Card Hoverable Bordered Class="card" Actions="@(new[] {
|
||||
info(()=> Info(context.Id)) ,
|
||||
delete(async ()=>await Delete(context.Id)) ,
|
||||
|
||||
})" Style="max-height:247px;">
|
||||
<CardMeta>
|
||||
<AvatarTemplate>
|
||||
|
||||
</AvatarTemplate>
|
||||
<TitleTemplate>
|
||||
<a>@context.Name</a>
|
||||
</TitleTemplate>
|
||||
<DescriptionTemplate>
|
||||
<Paragraph class="item" Ellipsis>
|
||||
<!--todo: Ellipsis not working-->
|
||||
@context.Describe
|
||||
</Paragraph>
|
||||
@if (context.Method == HttpMethodType.Get)
|
||||
{
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">Get</Tag>
|
||||
}
|
||||
else if (context.Method == HttpMethodType.Post)
|
||||
{
|
||||
<Tag Color="@PresetColor.Green.ToString()">Post</Tag>
|
||||
}
|
||||
</DescriptionTemplate>
|
||||
</CardMeta>
|
||||
</Card>
|
||||
}
|
||||
</ListItem>
|
||||
</AntList>
|
||||
</div>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
|
||||
@code
|
||||
{
|
||||
RenderFragment info(Action clickAction) =>@<a key="info" @onclick="@clickAction">查看</a>;
|
||||
RenderFragment delete(Action clickAction) => @<a key="delete" @onclick="@clickAction">删除</a> ;
|
||||
}
|
||||
69
AntSK/Pages/ApiPage/ApiList.razor.cs
Normal file
69
AntSK/Pages/ApiPage/ApiList.razor.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using AntDesign;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
|
||||
namespace AntSK.Pages.ApiPage
|
||||
{
|
||||
public partial class ApiList
|
||||
{
|
||||
private Apis [] _data = { };
|
||||
|
||||
[Inject]
|
||||
protected IApis_Repositories _apis_Repositories { get; set; }
|
||||
[Inject]
|
||||
IConfirmService _confirmService { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
await InitData("");
|
||||
}
|
||||
|
||||
private async Task InitData(string searchKey)
|
||||
{
|
||||
var list = new List<Apis> { new Apis() };
|
||||
List<Apis> data;
|
||||
if (string.IsNullOrEmpty(searchKey))
|
||||
{
|
||||
data = await _apis_Repositories.GetListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
data = await _apis_Repositories.GetListAsync(p => p.Name.Contains(searchKey));
|
||||
}
|
||||
|
||||
list.AddRange(data);
|
||||
_data = list.ToArray();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void NavigateToAddApp()
|
||||
{
|
||||
NavigationManager.NavigateTo("/plugins/api/add");
|
||||
}
|
||||
|
||||
private async Task Search(string searchKey)
|
||||
{
|
||||
await InitData(searchKey);
|
||||
}
|
||||
|
||||
private void Info(string id)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/plugins/api/add/{id}");
|
||||
}
|
||||
|
||||
private async Task Delete(string id)
|
||||
{
|
||||
var content = "是否确认删除此Api";
|
||||
var title = "删除";
|
||||
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
|
||||
if (result == ConfirmResult.Yes)
|
||||
{
|
||||
await _apis_Repositories.DeleteAsync(id);
|
||||
await InitData("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
132
AntSK/Pages/ApiPage/ApiList.razor.css
Normal file
132
AntSK/Pages/ApiPage/ApiList.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;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
@using AntSK.Models
|
||||
@page "/App/Add"
|
||||
@page "/App/Add/{AppId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="新增应用">
|
||||
<ChildContent>
|
||||
@@ -10,38 +12,60 @@
|
||||
<Form Model="@_appModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="知识库名称" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="知识库名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入知识库名称" @bind-Value="@context.Name" />
|
||||
</FormItem>
|
||||
<FormItem Label="图标" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="图标" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入图标" @bind-Value="@context.Icon" />
|
||||
<a href="https://antblazor.com/zh-CN/components/icon" target="_blank">图标库</a>
|
||||
</FormItem>
|
||||
<FormItem Label="类型" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.Type">
|
||||
<Radio RadioButton Value="@("chat")">简单对话</Radio>
|
||||
<Radio RadioButton Value="@("kms")" >知识库</Radio>
|
||||
<Radio RadioButton Value="@("chat")">会话应用</Radio>
|
||||
<Radio RadioButton Value="@("kms")">知识库</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem Label="描述" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
|
||||
</FormItem>
|
||||
@if (@context.Type == "chat")
|
||||
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
|
||||
</FormItem>
|
||||
@if (@context.Type == "chat")
|
||||
{
|
||||
<FormItem Label="提示词" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
|
||||
<FormItem Label="提示词" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<TextArea MinRows="4" Placeholder="请输入提示词,用户输入使用{{$input}} 来做占位符" @bind-Value="@context.Prompt" />
|
||||
</FormItem>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="温度系数" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<span>更确定</span>
|
||||
<Slider TValue="double" Style="display: inline-block;width: 300px; " Min="0" Max="100" DefaultValue="70" @bind-Value="@context.Temperature" />
|
||||
<span>更发散</span>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="API插件列表(选择后会开启自动调用)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select Mode="multiple"
|
||||
@bind-Values="apiIds"
|
||||
Placeholder="选择API插件"
|
||||
TItemValue="string"
|
||||
TItem="string"
|
||||
Size="@AntSizeLDSType.Default">
|
||||
<SelectOptions>
|
||||
@foreach (var api in _apiList)
|
||||
{
|
||||
<SelectOption TItem="string" TItemValue="string" Value="@api.Id" Label="@api.Name" />
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</FormItem>
|
||||
}
|
||||
@if (@context.Type == "kms")
|
||||
{
|
||||
<FormItem Label="知识库" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="知识库" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select Mode="multiple"
|
||||
@bind-Values="kmsIds"
|
||||
Placeholder="选择知识库"
|
||||
TItemValue="string"
|
||||
TItem="string"
|
||||
Size="@AntSizeLDSType.Default"
|
||||
>
|
||||
Size="@AntSizeLDSType.Default">
|
||||
<SelectOptions>
|
||||
@foreach (var kms in _kmsList)
|
||||
{
|
||||
@@ -51,21 +75,14 @@
|
||||
</Select>
|
||||
</FormItem>
|
||||
}
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="_submitFormLayout.WrapperCol">
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" HtmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
<Button OnClick="Back">
|
||||
<Button OnClick="Back">
|
||||
返回
|
||||
</Button>
|
||||
</FormItem>
|
||||
@if (!string.IsNullOrEmpty(_errorMsg))
|
||||
{
|
||||
<Alert Type="@AlertType.Error"
|
||||
Message="错误"
|
||||
Description="@_errorMsg"
|
||||
ShowIcon="true" />
|
||||
}
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
|
||||
@@ -16,8 +16,13 @@ namespace AntSK.Pages.AppPage
|
||||
|
||||
[Inject]
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected IApis_Repositories _apis_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
|
||||
private Apps _appModel = new Apps() ;
|
||||
|
||||
@@ -25,42 +30,25 @@ namespace AntSK.Pages.AppPage
|
||||
|
||||
private List<Kmss> _kmsList = new List<Kmss>();
|
||||
|
||||
IEnumerable<string> apiIds;
|
||||
|
||||
private string _errorMsg { get; set; }
|
||||
private List<Apis> _apiList = new List<Apis>();
|
||||
|
||||
private readonly FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
private readonly FormItemLayout _submitFormLayout = new FormItemLayout
|
||||
{
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
|
||||
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
|
||||
}
|
||||
};
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_kmsList = _kmss_Repositories.GetList();
|
||||
_apiList= _apis_Repositories.GetList();
|
||||
if (!string.IsNullOrEmpty(AppId))
|
||||
{
|
||||
//查看
|
||||
_appModel= _apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
kmsIds = _appModel.KmsIdList?.Split(",");
|
||||
apiIds= _appModel.ApiFunctionList?.Split(",");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private void HandleSubmit()
|
||||
{
|
||||
@@ -68,9 +56,11 @@ namespace AntSK.Pages.AppPage
|
||||
{
|
||||
//新增
|
||||
_appModel.Id = Guid.NewGuid().ToString();
|
||||
//秘钥
|
||||
_appModel.SecretKey="sk-"+ Guid.NewGuid().ToString();
|
||||
if (_apps_Repositories.IsAny(p => p.Name == _appModel.Name))
|
||||
{
|
||||
_errorMsg = "名称已存在!";
|
||||
_ = Message.Error("名称已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,7 +68,10 @@ namespace AntSK.Pages.AppPage
|
||||
{
|
||||
_appModel.KmsIdList = string.Join(",", kmsIds);
|
||||
}
|
||||
|
||||
if (apiIds != null && apiIds.Count() > 0)
|
||||
{
|
||||
_appModel.ApiFunctionList = string.Join(",", apiIds);
|
||||
}
|
||||
_apps_Repositories.Insert(_appModel);
|
||||
}
|
||||
else {
|
||||
@@ -87,7 +80,10 @@ namespace AntSK.Pages.AppPage
|
||||
{
|
||||
_appModel.KmsIdList = string.Join(",", kmsIds);
|
||||
}
|
||||
|
||||
if (apiIds != null && apiIds.Count() > 0)
|
||||
{
|
||||
_appModel.ApiFunctionList = string.Join(",", apiIds);
|
||||
}
|
||||
_apps_Repositories.Update(_appModel);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@page "/App/Detail/{AppID}"
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<h3>AppDetail</h3>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages.AppPage
|
||||
{
|
||||
public partial class AppDetail
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
@namespace AntSK.Pages
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@page "/AppList"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="应用列表">
|
||||
<Content>
|
||||
@@ -18,7 +20,7 @@
|
||||
<AntList TItem="Apps"
|
||||
DataSource="_data"
|
||||
ItemLayout="ListItemLayout.Horizontal"
|
||||
Grid="_listGridType">
|
||||
Grid="LayoutModel._listGridType">
|
||||
<ListItem NoFlex>
|
||||
@if (string.IsNullOrEmpty(context.Id))
|
||||
{
|
||||
@@ -30,7 +32,9 @@
|
||||
{
|
||||
<Card Hoverable Bordered Class="card" Actions="@(new[] {
|
||||
info(()=> Info(context.Id)) ,
|
||||
open(()=> Open(context.Id)),
|
||||
delete(async ()=>await Delete(context.Id)) ,
|
||||
|
||||
})" Style="max-height:247px;">
|
||||
<CardMeta>
|
||||
<AvatarTemplate>
|
||||
@@ -44,6 +48,15 @@
|
||||
<!--todo: Ellipsis not working-->
|
||||
@context.Describe
|
||||
</Paragraph>
|
||||
@if (context.Type == "chat")
|
||||
{
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">会话应用</Tag>
|
||||
}
|
||||
else if (context.Type == "kms")
|
||||
{
|
||||
<Tag Color="@PresetColor.Green.ToString()">知识库</Tag>
|
||||
|
||||
}
|
||||
</DescriptionTemplate>
|
||||
</CardMeta>
|
||||
</Card>
|
||||
@@ -57,5 +70,6 @@
|
||||
@code
|
||||
{
|
||||
RenderFragment info(Action clickAction) =>@<a key="info" @onclick="@clickAction">查看</a>;
|
||||
RenderFragment open(Action clickAction) =>@<a key="info" @onclick="@clickAction">外部使用</a>;
|
||||
RenderFragment delete(Action clickAction) => @<a key="delete" @onclick="@clickAction">删除</a> ;
|
||||
}
|
||||
|
||||
@@ -4,21 +4,10 @@ using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
|
||||
namespace AntSK.Pages
|
||||
namespace AntSK.Pages.AppPage
|
||||
{
|
||||
public partial class AppList
|
||||
{
|
||||
private readonly ListGridType _listGridType = new ListGridType
|
||||
{
|
||||
Gutter = 16,
|
||||
Xs = 1,
|
||||
Sm = 2,
|
||||
Md = 3,
|
||||
Lg = 3,
|
||||
Xl = 4,
|
||||
Xxl = 4
|
||||
};
|
||||
|
||||
private Apps [] _data = { };
|
||||
|
||||
[Inject]
|
||||
@@ -65,6 +54,10 @@ namespace AntSK.Pages
|
||||
NavigationManager.NavigateTo($"/app/add/{id}");
|
||||
}
|
||||
|
||||
private void Open(string id)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/app/open/{id}");
|
||||
}
|
||||
|
||||
|
||||
private async Task Delete(string id)
|
||||
|
||||
60
AntSK/Pages/AppPage/AppOpen.razor
Normal file
60
AntSK/Pages/AppPage/AppOpen.razor
Normal file
@@ -0,0 +1,60 @@
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@page "/App/Open/{AppID}"
|
||||
|
||||
<AntDesign.Col Lg="24" Md="24">
|
||||
<Card Class="tabsCard">
|
||||
<CardTabs>
|
||||
<Tabs DefaultActiveKey="1">
|
||||
<TabPane Key="1">
|
||||
<TabTemplate>Api接入</TabTemplate>
|
||||
<ChildContent>
|
||||
<Card>
|
||||
<Form Model="@_appModel"
|
||||
Style="margin-top: 8px;">
|
||||
<FormItem Label="接口地址" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input @bind-Value="@_openApiUrl" ReadOnly="true" />
|
||||
|
||||
</FormItem>
|
||||
<FormItem Label="秘钥" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input @bind-Value="@context.SecretKey" ReadOnly="true" />
|
||||
<Button OnClick="Reset" Style="margin-top:5px;">
|
||||
重置
|
||||
</Button>
|
||||
</FormItem>
|
||||
<FormItem Label="使用方法" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<TextArea @bind-Value="@_desc" ReadOnly="true" Rows="15" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
</TabPane>
|
||||
<TabPane Key="2">
|
||||
<TabTemplate>页面嵌入</TabTemplate>
|
||||
<ChildContent>
|
||||
<Form Model="@_appModel"
|
||||
Style="margin-top: 8px;">
|
||||
<FormItem Label="页面地址" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input @bind-Value="@_openChatUrl" ReadOnly="true" />
|
||||
<a href="@_openChatUrl" target="_blank">打开</a>
|
||||
</FormItem>
|
||||
<FormItem Label="嵌入脚本" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<TextArea @bind-Value="@_script" ReadOnly="true" Rows="5" />
|
||||
<span>
|
||||
将上述代码添加到站点的 body 中<br/>
|
||||
data-width:宽度,默认 30rem<br />
|
||||
data-height:高度,默认 50rem<br />
|
||||
data-message-icon-url:嵌入消息图标地址,默认不设置<br />
|
||||
data-color:消息图标颜色,默认 #4e83fd<br />
|
||||
</span>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</ChildContent>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</CardTabs>
|
||||
</Card>
|
||||
</AntDesign.Col>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
90
AntSK/Pages/AppPage/AppOpen.razor.cs
Normal file
90
AntSK/Pages/AppPage/AppOpen.razor.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AntSK.Pages.AppPage
|
||||
{
|
||||
public partial class AppOpen
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected IApps_Repositories _apps_Repositories { get; set; }
|
||||
[Inject]
|
||||
IConfirmService _confirmService { get; set; }
|
||||
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
|
||||
private readonly FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
|
||||
private Apps _appModel = new Apps();
|
||||
|
||||
private string _openApiUrl { get; set; }
|
||||
|
||||
private string _openChatUrl { get; set; }
|
||||
|
||||
private string _desc { get; set; }
|
||||
|
||||
private string _script { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_appModel = _apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
_openApiUrl = NavigationManager.BaseUri + "api/v1/chat/completions";
|
||||
_openChatUrl= NavigationManager.BaseUri + "openchat/"+AppId;
|
||||
GetDesc();
|
||||
GetScript();
|
||||
}
|
||||
|
||||
private void GetDesc()
|
||||
{
|
||||
_desc = @$"为了方便其他应用对接,接口符合openai规范,省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "你好,你是谁" } } }, Formatting.Indented)}";
|
||||
}
|
||||
|
||||
private void GetScript()
|
||||
{
|
||||
_script = $"<script src=\"{NavigationManager.BaseUri}js/iframe.js\" data-width=\"40rem\" data-height=\"80vh\" id=\"antsk-iframe\" data-src=\"{NavigationManager.BaseUri}openchat/{AppId}\" data-color=\"#4e83fd\" data-message-icon-url=\"{NavigationManager.BaseUri}assets/ai.png\"></script>";
|
||||
}
|
||||
|
||||
private void HandleSubmit()
|
||||
{
|
||||
}
|
||||
|
||||
private async Task Reset()
|
||||
{
|
||||
var content = "是否确认重置秘钥,重之后之前的秘钥将无法访问API接口";
|
||||
var title = "重置";
|
||||
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
|
||||
if (result == ConfirmResult.Yes)
|
||||
{
|
||||
_appModel.SecretKey = "sk-" + Guid.NewGuid().ToString();
|
||||
_apps_Repositories.Update(_appModel);
|
||||
GetDesc();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,15 @@
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@page "/Chat"
|
||||
@page "/Chat/{AppId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<GridRow Gutter="(16, 16)">
|
||||
<GridCol Span="12">
|
||||
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
|
||||
<Card Style="height:800px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="setting" /> 选择应用
|
||||
<GridRow Gutter="(16, 16)">
|
||||
<GridCol Span="12">
|
||||
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
|
||||
<Card Style="height:700px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="setting" /> 选择应用
|
||||
<Select DataSource="@_list"
|
||||
@bind-Value="@AppId"
|
||||
DefaultValue="@("lucy")"
|
||||
@@ -18,67 +20,133 @@
|
||||
LabelProperty="c=>c.Name"
|
||||
Style="width:200px">
|
||||
</Select>
|
||||
</TitleTemplate>
|
||||
<Body>
|
||||
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
|
||||
<GridRow Gutter="(8, 8)">
|
||||
<Virtualize Items="@(MessageList.OrderByDescending(o => o.CreateTime).ToList())" Context="item">
|
||||
<GridCol Span="24">
|
||||
<Card Size="small">
|
||||
<TitleTemplate>
|
||||
<Text Strong><Icon Type="bulb" /> @(item.Questions)</Text>
|
||||
</TitleTemplate>
|
||||
<Extra>
|
||||
<Space>
|
||||
<SpaceItem>
|
||||
<a style="color: gray;" @onclick="@(() => OnCopyAsync(item))"><Icon Type="copy" /></a>
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<a style="color: gray;" @onclick="@(() => OnClearAsync(item.ID))"><Icon Type="rest" /></a>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</Extra>
|
||||
<Body>
|
||||
@((MarkupString)(item.HtmlAnswers))
|
||||
</Body>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</Virtualize>
|
||||
</GridRow>
|
||||
</div>
|
||||
<div style="height: 10px;"></div>
|
||||
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())">
|
||||
<Suffix>
|
||||
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())"></Button>
|
||||
</Suffix>
|
||||
</AntDesign.Input>
|
||||
</Body>
|
||||
</Card>
|
||||
</Spin>
|
||||
</GridCol>
|
||||
<GridCol Span="12">
|
||||
<Card Style="height: 800px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="search" /> 调试结果
|
||||
</TitleTemplate>
|
||||
<Extra>
|
||||
|
||||
</Extra>
|
||||
<Body>
|
||||
<AntList Bordered DataSource="@RelevantSources">
|
||||
<ChildContent Context="item">
|
||||
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
|
||||
<ListItem>
|
||||
@item.Text
|
||||
</ListItem>
|
||||
</ChildContent>
|
||||
</AntList>
|
||||
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
|
||||
<GridRow Gutter="(8, 8)" Style="margin:0">
|
||||
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
|
||||
@if (item.IsSend)
|
||||
{
|
||||
<GridRow Style="width:100%">
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble sent">
|
||||
@(item.Context)
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
else
|
||||
{
|
||||
<GridRow Style="width:100%">
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" />
|
||||
</GridCol>
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble received">
|
||||
@((MarkupString)(item.HtmlAnswers))
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
</Virtualize>
|
||||
</GridRow>
|
||||
</div>
|
||||
<div style="height: 10px;"></div>
|
||||
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
|
||||
<Suffix>
|
||||
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
|
||||
</Suffix>
|
||||
</AntDesign.Input>
|
||||
</Body>
|
||||
</Card>
|
||||
</Spin>
|
||||
</GridCol>
|
||||
<GridCol Span="12">
|
||||
<Card Style="height: 700px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="search" /> 调试结果
|
||||
</TitleTemplate>
|
||||
<Extra>
|
||||
|
||||
</Extra>
|
||||
<Body>
|
||||
<AntList Bordered DataSource="@RelevantSources" Style="padding:10px;">
|
||||
<ChildContent Context="item" >
|
||||
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
|
||||
<Body>
|
||||
@((MarkupString)(@item.Text))
|
||||
</Body>
|
||||
</ChildContent>
|
||||
</AntList>
|
||||
</Body>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
|
||||
<style>
|
||||
|
||||
@code {
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
width: 350px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
margin-bottom: 0;
|
||||
border-radius: 5px;
|
||||
max-width: 70%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.received {
|
||||
background-color: #f0f0f0;
|
||||
align-self: flex-start;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.sent {
|
||||
background-color: #daf8cb;
|
||||
align-self: flex-end;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: block;
|
||||
font-size: 0.75em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.received .timestamp {
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sent .timestamp {
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
@code {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Core;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
@@ -20,23 +26,28 @@ namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
[Inject]
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
[Inject]
|
||||
protected IApps_Repositories _apps_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IApis_Repositories _apis_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected MemoryServerless _memory { get; set; }
|
||||
//[Inject]
|
||||
//protected Kernel _kernel { get; set; }
|
||||
[Inject]
|
||||
protected Kernel _kernel { get; set; }
|
||||
protected IKernelService _kernelService { get; set; }
|
||||
|
||||
protected bool _loading = false;
|
||||
protected List<MessageInfo> MessageList = [];
|
||||
protected string? _messageInput;
|
||||
protected string _json = "";
|
||||
protected bool Sendding = false;
|
||||
|
||||
List<RelevantSource> RelevantSources = new List<RelevantSource>();
|
||||
|
||||
@@ -44,31 +55,49 @@ namespace AntSK.Pages.ChatPage
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_list= _apps_Repositories.GetList();
|
||||
_list = _apps_Repositories.GetList();
|
||||
}
|
||||
protected async Task OnSendAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_messageInput))
|
||||
try
|
||||
{
|
||||
_ = Message.Info("请输入消息", 2);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_messageInput))
|
||||
{
|
||||
_ = Message.Info("请输入消息", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(AppId))
|
||||
if (string.IsNullOrWhiteSpace(AppId))
|
||||
{
|
||||
_ = Message.Info("请选择应用进行测试", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
MessageList.Add(new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Context = _messageInput,
|
||||
CreateTime = DateTime.Now,
|
||||
IsSend = true
|
||||
});
|
||||
|
||||
|
||||
Sendding = true;
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
Sendding = false;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
_ = Message.Info("请选择应用进行测试", 2);
|
||||
return;
|
||||
Sendding = false;
|
||||
_ = Message.Error("异常:"+ex.Message, 2);
|
||||
}
|
||||
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
|
||||
}
|
||||
protected async Task OnCopyAsync(MessageInfo item)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_messageInput = item.Questions;
|
||||
_messageInput = item.Context;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,82 +111,199 @@ namespace AntSK.Pages.ChatPage
|
||||
|
||||
protected async Task<bool> SendAsync(string questions)
|
||||
{
|
||||
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
string msg = "";
|
||||
//处理多轮会话
|
||||
if (MessageList.Count > 0)
|
||||
{
|
||||
msg = await HistorySummarize(questions);
|
||||
}
|
||||
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
switch (app.Type)
|
||||
{
|
||||
case "chat":
|
||||
//普通会话
|
||||
var promptTemplateFactory = new KernelPromptTemplateFactory();
|
||||
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
|
||||
var renderedPrompt = await promptTemplate.RenderAsync(_kernel);
|
||||
|
||||
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, new OpenAIPromptExecutionSettings() );
|
||||
var chatResult = await _kernel.InvokeAsync(func,new KernelArguments() { ["input"]=questions});
|
||||
if (chatResult.IsNotNull())
|
||||
{
|
||||
string answers = chatResult.GetValue<string>();
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Questions = questions,
|
||||
Answers = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info);
|
||||
}
|
||||
|
||||
await SendChat(questions, msg, app);
|
||||
break;
|
||||
case "kms":
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(questions, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Questions = questions,
|
||||
Answers = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info);
|
||||
}
|
||||
|
||||
foreach (var x in kmsResult.RelevantSources)
|
||||
{
|
||||
foreach (var xsd in x.Partitions)
|
||||
{
|
||||
string sourceName = x.SourceName;
|
||||
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == x.SourceName);
|
||||
if (fileDetail.IsNotNull())
|
||||
{
|
||||
sourceName = fileDetail.FileName;
|
||||
}
|
||||
RelevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = xsd.Text, Relevance = xsd.Relevance });
|
||||
}
|
||||
}
|
||||
}
|
||||
await SendKms(questions, msg, app);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
|
||||
string dataMsg = "";
|
||||
if (xlresult != null)
|
||||
{
|
||||
foreach (var item in xlresult.Results)
|
||||
{
|
||||
foreach (var part in item.Partitions)
|
||||
{
|
||||
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance*100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
|
||||
//输出调试信息
|
||||
var markdown = new Markdown();
|
||||
string sourceName = item.SourceName;
|
||||
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == item.SourceName);
|
||||
if (fileDetail.IsNotNull())
|
||||
{
|
||||
sourceName = fileDetail.FileName;
|
||||
}
|
||||
RelevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = markdown.Transform(part.Text), Relevance = part.Relevance });
|
||||
}
|
||||
}
|
||||
|
||||
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
|
||||
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: jsonFun,
|
||||
arguments: new KernelArguments() { ["data"] = dataMsg, ["history"] = msg, ["questions"]=questions });
|
||||
|
||||
MessageInfo info = null;
|
||||
var markdown1 = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.HtmlAnswers += content.Content;
|
||||
await Task.Delay(50);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown1.Transform(info.HtmlAnswers);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendChat(string questions, string msg, Apps app)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
if (string.IsNullOrEmpty(app.Prompt)||!app.Prompt.Contains("{{$input}}"))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = app.Prompt.ConvertToString()+"{{$input}}";
|
||||
}
|
||||
var temperature = app.Temperature/100;//存的是0~100需要缩小
|
||||
OpenAIPromptExecutionSettings settings = new() {Temperature= temperature };
|
||||
if (!string.IsNullOrEmpty(app.ApiFunctionList))
|
||||
{
|
||||
_kernelService.ImportFunctionsByApp(app, _kernel);
|
||||
settings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,Temperature = temperature };
|
||||
}
|
||||
|
||||
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
|
||||
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
MessageInfo info = null;
|
||||
var markdown = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.HtmlAnswers += content.Content;
|
||||
await Task.Delay(50);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(string questions)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
if (MessageList.Count > 1)
|
||||
{
|
||||
StringBuilder history = new StringBuilder();
|
||||
foreach (var item in MessageList)
|
||||
{
|
||||
if (item.IsSend)
|
||||
{
|
||||
history.Append($"user:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Append($"assistant:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
if (MessageList.Count > 10)
|
||||
{
|
||||
//历史会话大于10条,进行总结
|
||||
var msg = await _kernelService.HistorySummarize(_kernel, questions, history.ToString());
|
||||
return msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = $"history:{history.ToString()}{Environment.NewLine} user:{questions}"; ;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return questions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RelevantSource
|
||||
|
||||
114
AntSK/Pages/ChatPage/OpenChat.razor
Normal file
114
AntSK/Pages/ChatPage/OpenChat.razor
Normal file
@@ -0,0 +1,114 @@
|
||||
@namespace AntSK.Pages.ChatPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@page "/OpenChat/{AppId}"
|
||||
@layout OpenLayout
|
||||
|
||||
<div id="chat" style="display:flex; flex-direction:column; height:100%; overflow-x:hidden;">
|
||||
<PageHeader Class="site-page-header" Title="@app.Name" Subtitle="@app.Describe" />
|
||||
<div id="scrollDiv" style="flex:1; width:100%; overflow-y:auto; overflow-x:hidden;padding:10px;">
|
||||
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
|
||||
@if (item.IsSend)
|
||||
{
|
||||
<GridRow>
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble sent">
|
||||
@(item.Context)
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
else
|
||||
{
|
||||
<GridRow>
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" />
|
||||
</GridCol>
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble received">
|
||||
@((MarkupString)(item.HtmlAnswers))
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
|
||||
</Virtualize>
|
||||
</div>
|
||||
<div style="flex-shrink:0;margin:10px;">
|
||||
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
|
||||
<Suffix>
|
||||
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
|
||||
</Suffix>
|
||||
</AntDesign.Input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
width: 350px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
margin-bottom: 0;
|
||||
border-radius: 5px;
|
||||
max-width: 70%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.received {
|
||||
background-color: #f0f0f0;
|
||||
align-self: flex-start;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.sent {
|
||||
background-color: #daf8cb;
|
||||
align-self: flex-end;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: block;
|
||||
font-size: 0.75em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.received .timestamp {
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sent .timestamp {
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
285
AntSK/Pages/ChatPage/OpenChat.razor.cs
Normal file
285
AntSK/Pages/ChatPage/OpenChat.razor.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Core;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Text;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
using AntSK.Domain.Domain.Service;
|
||||
|
||||
namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
public partial class OpenChat
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
[Inject]
|
||||
protected IApps_Repositories _apps_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected MemoryServerless _memory { get; set; }
|
||||
[Inject]
|
||||
protected IKernelService _kernelService { get; set; }
|
||||
|
||||
protected bool _loading = false;
|
||||
protected List<MessageInfo> MessageList = [];
|
||||
protected string? _messageInput;
|
||||
protected string _json = "";
|
||||
protected bool Sendding = false;
|
||||
|
||||
protected Apps app = new Apps();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
app = _apps_Repositories.GetFirst(p=>p.Id==AppId);
|
||||
}
|
||||
protected async Task OnSendAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_messageInput))
|
||||
{
|
||||
_ = Message.Info("请输入消息", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
MessageList.Add(new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Context = _messageInput,
|
||||
CreateTime = DateTime.Now,
|
||||
IsSend = true
|
||||
});
|
||||
|
||||
|
||||
Sendding = true;
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
Sendding = false;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Sendding = false;
|
||||
_ = Message.Error("异常:"+ex.Message, 2);
|
||||
}
|
||||
|
||||
}
|
||||
protected async Task OnCopyAsync(MessageInfo item)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_messageInput = item.Context;
|
||||
});
|
||||
}
|
||||
|
||||
protected async Task OnClearAsync(string id)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
MessageList = MessageList.Where(w => w.ID != id).ToList();
|
||||
});
|
||||
}
|
||||
|
||||
protected async Task<bool> SendAsync(string questions)
|
||||
{
|
||||
string msg = "";
|
||||
//处理多轮会话
|
||||
if (MessageList.Count > 0)
|
||||
{
|
||||
msg = await HistorySummarize(questions);
|
||||
}
|
||||
|
||||
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
switch (app.Type)
|
||||
{
|
||||
case "chat":
|
||||
//普通会话
|
||||
await SendChat(questions, msg, app);
|
||||
break;
|
||||
case "kms":
|
||||
//知识库问答
|
||||
await SendKms(questions, msg, app);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
|
||||
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
|
||||
string dataMsg = "";
|
||||
if (xlresult != null)
|
||||
{
|
||||
foreach (var item in xlresult.Results)
|
||||
{
|
||||
foreach (var part in item.Partitions)
|
||||
{
|
||||
dataMsg += $"[file:{item.SourceName};Relevance:{(part.Relevance * 100).ToString("F2")}%]:{part.Text}{Environment.NewLine}";
|
||||
}
|
||||
}
|
||||
KernelFunction jsonFun = _kernel.Plugins.GetFunction("KMSPlugin", "Ask");
|
||||
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: jsonFun,
|
||||
arguments: new KernelArguments() { ["data"] = dataMsg, ["history"] = msg, ["questions"] = questions });
|
||||
|
||||
MessageInfo info = null;
|
||||
var markdown1 = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.HtmlAnswers += content.Content;
|
||||
await Task.Delay(50);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown1.Transform(info.HtmlAnswers);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendChat(string questions, string msg, Apps app)
|
||||
{
|
||||
var _kernel= _kernelService.GetKernel();
|
||||
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
|
||||
}
|
||||
//注册插件
|
||||
var temperature = app.Temperature / 100;//存的是0~100需要缩小
|
||||
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
|
||||
if (!string.IsNullOrEmpty(app.ApiFunctionList))
|
||||
{
|
||||
_kernelService.ImportFunctionsByApp(app, _kernel);
|
||||
settings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, Temperature = temperature };
|
||||
}
|
||||
|
||||
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
|
||||
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
MessageInfo info = null;
|
||||
var markdown = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.HtmlAnswers += content.Content;
|
||||
await Task.Delay(50);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(string questions)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
if (MessageList.Count > 1)
|
||||
{
|
||||
StringBuilder history = new StringBuilder();
|
||||
foreach (var item in MessageList)
|
||||
{
|
||||
if (item.IsSend)
|
||||
{
|
||||
history.Append($"user:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Append($"assistant:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
if (MessageList.Count > 10)
|
||||
{
|
||||
//历史会话大于10条,进行总结
|
||||
var msg = await _kernelService.HistorySummarize(_kernel, questions, history.ToString());
|
||||
return msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = $"history:{history.ToString()}{Environment.NewLine} user:{questions}"; ;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return questions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
AntSK/Pages/ChatPage/OpenChat.razor.css
Normal file
3
AntSK/Pages/ChatPage/OpenChat.razor.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.ant-card-body {
|
||||
height: 90% !important;
|
||||
}
|
||||
@@ -1,10 +1,27 @@
|
||||
@namespace AntSK.Pages.Exception
|
||||
@page "/exception/404"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<Result Status="404"
|
||||
Title="404"
|
||||
SubTitle="Sorry, the page you visited does not exist.">
|
||||
<Extra>
|
||||
<Button Type="primary">Back Home</Button>
|
||||
</Extra>
|
||||
</Result>
|
||||
<div class="rail">
|
||||
@for (var i = 0; i < StampCount; i++)
|
||||
{
|
||||
<div class="@((i % 2 == 0) ? "stamp zero" : "stamp four")">
|
||||
@(i % 2 == 0 ? "4" : "0")
|
||||
</div>
|
||||
}
|
||||
<div class="world">
|
||||
<div class="forward">
|
||||
<div class="box">
|
||||
@for (var i = 0; i < 6; i++)
|
||||
{
|
||||
<div class="wall"></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private int StampCount { get; set; } = 20; // 初始的循环次数
|
||||
}
|
||||
|
||||
245
AntSK/Pages/Exception/404/404.razor.css
Normal file
245
AntSK/Pages/Exception/404/404.razor.css
Normal file
@@ -0,0 +1,245 @@
|
||||
body {
|
||||
background: #fff;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
font-family: "Anton", sans-serif;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
div {
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.rail {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: rotateX(-30deg) rotateY(-50deg);
|
||||
}
|
||||
|
||||
.rail .stamp {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgb(20, 20, 20);
|
||||
color: #fff;
|
||||
font-size: 7rem;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(1) {
|
||||
animation: stampSlide 40000ms -2300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(2) {
|
||||
animation: stampSlide 40000ms -4300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(3) {
|
||||
animation: stampSlide 40000ms -6300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(4) {
|
||||
animation: stampSlide 40000ms -8300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(5) {
|
||||
animation: stampSlide 40000ms -10300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(6) {
|
||||
animation: stampSlide 40000ms -12300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(7) {
|
||||
animation: stampSlide 40000ms -14300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(8) {
|
||||
animation: stampSlide 40000ms -16300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(9) {
|
||||
animation: stampSlide 40000ms -18300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(10) {
|
||||
animation: stampSlide 40000ms -20300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(11) {
|
||||
animation: stampSlide 40000ms -22300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(12) {
|
||||
animation: stampSlide 40000ms -24300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(13) {
|
||||
animation: stampSlide 40000ms -26300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(14) {
|
||||
animation: stampSlide 40000ms -28300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(15) {
|
||||
animation: stampSlide 40000ms -30300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(16) {
|
||||
animation: stampSlide 40000ms -32300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(17) {
|
||||
animation: stampSlide 40000ms -34300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(18) {
|
||||
animation: stampSlide 40000ms -36300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(19) {
|
||||
animation: stampSlide 40000ms -38300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(20) {
|
||||
animation: stampSlide 40000ms -40300ms linear infinite;
|
||||
}
|
||||
|
||||
@keyframes stampSlide {
|
||||
0% {
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
|
||||
}
|
||||
}
|
||||
|
||||
.world .forward {
|
||||
position: absolute;
|
||||
animation: slide 2000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
transform-origin: 100% 100%;
|
||||
animation: roll 2000ms cubic-bezier(1, 0.01, 1, 1) infinite;
|
||||
}
|
||||
|
||||
.world .box .wall {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
border: 1px solid rgb(250, 250, 250);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.world .box .wall::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 7rem;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(1) {
|
||||
transform: translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(2) {
|
||||
transform: rotateX(180deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(3) {
|
||||
transform: rotateX(90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(3)::before {
|
||||
transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms -2000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(4) {
|
||||
transform: rotateX(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(4)::before {
|
||||
transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms -2000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(5) {
|
||||
transform: rotateY(90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(5)::before {
|
||||
transform: rotateX(180deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(6) {
|
||||
transform: rotateY(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(6)::before {
|
||||
transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms linear infinite;
|
||||
}
|
||||
|
||||
@keyframes zeroFour {
|
||||
0% {
|
||||
content: "4";
|
||||
}
|
||||
|
||||
100% {
|
||||
content: "0";
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes roll {
|
||||
0% {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
|
||||
85% {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
87% {
|
||||
transform: rotateZ(88deg);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-200px);
|
||||
}
|
||||
}
|
||||
41
AntSK/Pages/Index.razor
Normal file
41
AntSK/Pages/Index.razor
Normal file
@@ -0,0 +1,41 @@
|
||||
@namespace AntSK.Pages
|
||||
@using MarkdownSharp
|
||||
@page "/"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<Body>
|
||||
@((MarkupString)(body))
|
||||
<Image Width="200px" Src="@imgSrc" />
|
||||
</Body>
|
||||
|
||||
@code {
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
private string imgSrc { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
imgSrc = NavigationManager.BaseUri + "assets/gzh.png";
|
||||
}
|
||||
private string body = new Markdown().Transform(@"非常感谢您对**AntSK**的喜爱,您的支持对我来说是莫大的鼓励。
|
||||
|
||||
如果您对技术类视频感兴趣,欢迎关注我的B站账号,获取更多精彩内容。
|
||||
|
||||
- **Bilibili账号**:[点击这里](https://space.bilibili.com/1673184683/channel/series)
|
||||
|
||||
同时,如果您感兴趣,可以访问我的GitHub页面,查看更多开源项目和代码:
|
||||
|
||||
- **GitHub页面**:[点击这里](https://github.com/xuzeyu91)
|
||||
|
||||
另外,如果您想加入由我创建的**.Net/AI应用开发微信交流群**,欢迎先添加我的微信**(xuzeyu91)**。
|
||||
|
||||
发送“进群请求”,我将会把您邀请进群。
|
||||
|
||||
期待与您在线上交流技术,共同进步!
|
||||
|
||||
也欢迎您关注我的公众号,后面可以第一时间知道**AntSK**项目进展。");
|
||||
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
@using AntSK.Models
|
||||
@page "/Kms/Add"
|
||||
@inject IMessageService _message
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="新增知识库">
|
||||
<ChildContent>
|
||||
@@ -11,29 +13,31 @@
|
||||
Model="@_kmsModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="知识库名称" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="知识库名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入知识库名称" @bind-Value="@context.Name" />
|
||||
</FormItem>
|
||||
<FormItem Label="图标" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="图标" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入图标" @bind-Value="@context.Icon" />
|
||||
<a href="https://antblazor.com/zh-CN/components/icon" target="_blank">图标库</a>
|
||||
</FormItem>
|
||||
<FormItem Label="描述" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<FormItem Label="描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入描述" @bind-Value="@context.Describe" />
|
||||
</FormItem>
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="_submitFormLayout.WrapperCol">
|
||||
<FormItem Label="段落切片数(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<AntDesign.InputNumber @bind-Value="context.MaxTokensPerParagraph" PlaceHolder="段落切片数"></AntDesign.InputNumber>
|
||||
</FormItem>
|
||||
<FormItem Label="行切片数(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<AntDesign.InputNumber @bind-Value="context.MaxTokensPerLine" PlaceHolder="行切片数"></AntDesign.InputNumber>
|
||||
</FormItem>
|
||||
<FormItem Label="重叠部分(token)" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<AntDesign.InputNumber @bind-Value="context.OverlappingTokens" PlaceHolder="重叠部分"></AntDesign.InputNumber>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" HtmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
</FormItem>
|
||||
@if ( !string.IsNullOrEmpty(_errorMsg))
|
||||
{
|
||||
<Alert Type="@AlertType.Error"
|
||||
Message="错误"
|
||||
Description="@_errorMsg"
|
||||
ShowIcon="true"
|
||||
/>
|
||||
}
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
|
||||
@@ -13,41 +13,17 @@ namespace AntSK.Pages.KmsPage
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; }
|
||||
|
||||
private string _errorMsg { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
|
||||
private readonly Kmss _kmsModel = new Kmss() ;
|
||||
|
||||
private readonly FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
private readonly FormItemLayout _submitFormLayout = new FormItemLayout
|
||||
{
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
|
||||
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
|
||||
}
|
||||
};
|
||||
|
||||
private void HandleSubmit()
|
||||
{
|
||||
_kmsModel.Id = Guid.NewGuid().ToString();
|
||||
if (_kmss_Repositories.IsAny(p => p.Name == _kmsModel.Name))
|
||||
{
|
||||
_errorMsg = "名称已存在!";
|
||||
_ = Message.Error("名称已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@page "/Kms/Detail/{KmsID}"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<div>
|
||||
<PageContainer Title="知识库文档">
|
||||
@@ -17,8 +19,9 @@
|
||||
<Dropdown Style="position: absolute; right: 20px; margin-bottom: 8px;">
|
||||
<Overlay>
|
||||
<Menu>
|
||||
@( _fileUpload( () =>FileShowModal()))
|
||||
@(_fileUpload(() => FileShowModal()))
|
||||
@(_urlUpload(() => UrlShowModal()))
|
||||
@(_textUpload(() => TextShowModal()))
|
||||
</Menu>
|
||||
</Overlay>
|
||||
<ChildContent>
|
||||
@@ -27,20 +30,20 @@
|
||||
</Dropdown>
|
||||
<div class="extraContent" style="margin-right:100px;">
|
||||
<Search Class="extraContentSearch" Placeholder="搜索文档" @bind-Value="_model.Id" />
|
||||
</div>
|
||||
|
||||
</Extra>
|
||||
<ChildContent >
|
||||
<AntList TItem="KmsDetails"
|
||||
DataSource="_data"
|
||||
ItemLayout="ListItemLayout.Horizontal">
|
||||
<ListItem Actions="@(new[] {
|
||||
</div>
|
||||
|
||||
</Extra>
|
||||
<ChildContent>
|
||||
<AntList TItem="KmsDetails"
|
||||
DataSource="_data"
|
||||
ItemLayout="ListItemLayout.Horizontal">
|
||||
<ListItem Actions="@(new[] {
|
||||
detail(()=> FileDetail(context.Id)) ,
|
||||
delete(async ()=>await DeleteFile(context.Id)) ,
|
||||
})">
|
||||
<ListItemMeta Description="@context.Id">
|
||||
<TitleTemplate>
|
||||
<div >文件ID</div>
|
||||
<div>文件ID</div>
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
<ListItemMeta Description="@context.Type">
|
||||
@@ -48,15 +51,15 @@
|
||||
<div>文件类型</div>
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
@if(@context.Type=="file")
|
||||
@if (@context.Type == "file")
|
||||
{
|
||||
<ListItemMeta Avatar="" Description="@context.FileName">
|
||||
<TitleTemplate>
|
||||
<a >文件名称</a>
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
<ListItemMeta Avatar="" Description="@context.FileName">
|
||||
<TitleTemplate>
|
||||
<a>文件名称</a>
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
}
|
||||
else
|
||||
else if (@context.Type == "url")
|
||||
{
|
||||
<ListItemMeta Avatar="" Description="@context.Url">
|
||||
<TitleTemplate>
|
||||
@@ -64,10 +67,17 @@
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
}
|
||||
|
||||
else if (@context.Type == "text")
|
||||
{
|
||||
<ListItemMeta Avatar="" Description="……">
|
||||
<TitleTemplate>
|
||||
<a>文本内容</a>
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
}
|
||||
<ListItemMeta Avatar="" Description="@context.DataCount.ToString()">
|
||||
<TitleTemplate>
|
||||
<a >文档切片数量</a>
|
||||
<a>文档切片数量</a>
|
||||
</TitleTemplate>
|
||||
</ListItemMeta>
|
||||
<div class="listContent">
|
||||
@@ -100,6 +110,21 @@
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal Title="文本导入"
|
||||
Visible="@_textVisible"
|
||||
OnOk="@TextHandleOk"
|
||||
OnCancel="@TextHandleCancel"
|
||||
ConfirmLoading="@_textConfirmLoading">
|
||||
<Form Model="@textModel"
|
||||
LabelColSpan="8"
|
||||
WrapperColSpan="16"
|
||||
@ref="@_textForm">
|
||||
<FormItem Label="文本内容">
|
||||
<TextArea @bind-Value="@context.Text" Rows="5" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal Title="文件导入"
|
||||
Visible="@_fileVisible"
|
||||
OnOk="@FileHandleOk"
|
||||
@@ -118,33 +143,40 @@
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
||||
application/pdf,
|
||||
application/json,
|
||||
text/markdown"
|
||||
text/markdown,
|
||||
text/x-markdown"
|
||||
BeforeUpload="BeforeUpload"
|
||||
OnSingleCompleted="OnSingleCompleted">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<Icon Type="inbox" />
|
||||
</p>
|
||||
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||
<p class="ant-upload-hint">
|
||||
支持txt、word、pdf、md、excel、ppt等文件。
|
||||
</p>
|
||||
</Upload>
|
||||
<p class="ant-upload-drag-icon">
|
||||
<Icon Type="inbox" />
|
||||
</p>
|
||||
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||
<p class="ant-upload-hint">
|
||||
支持txt、word、pdf、md、excel、ppt等文件。
|
||||
</p>
|
||||
</Upload>
|
||||
</Modal>
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
RenderFragment _fileUpload(Action clickAction) =>@<MenuItem>
|
||||
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
|
||||
文件导入
|
||||
</a>
|
||||
</MenuItem>;
|
||||
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
|
||||
文件导入
|
||||
</a>
|
||||
</MenuItem>;
|
||||
|
||||
RenderFragment _urlUpload(Action clickAction) =>@<MenuItem>
|
||||
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
|
||||
链接读取
|
||||
</a>
|
||||
</MenuItem>;
|
||||
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
|
||||
链接读取
|
||||
</a>
|
||||
</MenuItem>;
|
||||
|
||||
RenderFragment _textUpload(Action clickAction) =>@<MenuItem>
|
||||
<a target="_blank" rel="noopener noreferrer" @onclick="@clickAction">
|
||||
文本导入
|
||||
</a>
|
||||
</MenuItem>;
|
||||
|
||||
RenderFragment detail(Action clickAction) => @<a key="detail" @onclick="@clickAction">详情</a>;
|
||||
RenderFragment delete(Action clickAction) => @<a key="edit" @onclick="@clickAction">删除</a>;
|
||||
|
||||
@@ -31,12 +31,18 @@ namespace AntSK.Pages.KmsPage
|
||||
bool _fileVisible = false;
|
||||
bool _fileConfirmLoading = false;
|
||||
|
||||
bool _textVisible = false;
|
||||
bool _textConfirmLoading = false;
|
||||
|
||||
string filePath;
|
||||
string fileName;
|
||||
|
||||
private Form<UrlModel> _urlForm;
|
||||
private UrlModel urlModel = new UrlModel();
|
||||
|
||||
private Form<TextModel> _textForm;
|
||||
private TextModel textModel = new TextModel();
|
||||
|
||||
private readonly IDictionary<string, ProgressStatus> _pStatus = new Dictionary<string, ProgressStatus>
|
||||
{
|
||||
{"active", ProgressStatus.Active},
|
||||
@@ -51,8 +57,11 @@ namespace AntSK.Pages.KmsPage
|
||||
protected IConfirmService _confirmService { get; set; }
|
||||
[Inject]
|
||||
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected MemoryServerless _memory { get; set; }
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
|
||||
private MemoryServerless _memory { get; set; }
|
||||
[Inject]
|
||||
protected IKMService iKMService { get; set; }
|
||||
[Inject]
|
||||
@@ -63,6 +72,13 @@ namespace AntSK.Pages.KmsPage
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_data =await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
|
||||
var km = _kmss_Repositories.GetFirst(p => p.Id == KmsId);
|
||||
//使用知识库设置的参数,
|
||||
_memory = iKMService.GetMemory(textPartitioningOptions:new Microsoft.KernelMemory.Configuration.TextPartitioningOptions() {
|
||||
MaxTokensPerLine= km.MaxTokensPerLine,
|
||||
MaxTokensPerParagraph=km.MaxTokensPerParagraph ,
|
||||
OverlappingTokens=km.OverlappingTokens
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据文档ID获取文档
|
||||
@@ -117,10 +133,56 @@ namespace AntSK.Pages.KmsPage
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Text
|
||||
|
||||
public class TextModel
|
||||
{
|
||||
[Required]
|
||||
public string Text { get; set; }
|
||||
}
|
||||
private async Task TextHandleOk(MouseEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_textConfirmLoading = true;
|
||||
string fileid = Guid.NewGuid().ToString();
|
||||
await _memory.ImportTextAsync(textModel.Text, fileid, new TagCollection() { { "kmsid", KmsId } }
|
||||
, index: "kms");
|
||||
//查询文档数量
|
||||
var docTextList = await iKMService.GetDocumentByFileID(fileid);
|
||||
|
||||
KmsDetails detial = new KmsDetails()
|
||||
{
|
||||
Id = fileid,
|
||||
KmsId = KmsId,
|
||||
Type = "text",
|
||||
DataCount = docTextList.Count,
|
||||
CreateTime = DateTime.Now
|
||||
};
|
||||
await _kmsDetails_Repositories.InsertAsync(detial);
|
||||
_data = await _kmsDetails_Repositories.GetListAsync(p => p.KmsId == KmsId);
|
||||
|
||||
_textVisible = false;
|
||||
_textConfirmLoading = false;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
|
||||
}
|
||||
}
|
||||
private void TextHandleCancel(MouseEventArgs e)
|
||||
{
|
||||
_textVisible = false;
|
||||
}
|
||||
private void TextShowModal()
|
||||
{
|
||||
_textVisible = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
|
||||
|
||||
|
||||
private async Task FileHandleOk(MouseEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -177,10 +239,11 @@ namespace AntSK.Pages.KmsPage
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"application/pdf",
|
||||
"application/json",
|
||||
"text/x-markdown",
|
||||
"text/markdown"
|
||||
};
|
||||
var IsType = types.Contains( file.Type );
|
||||
if (!IsType)
|
||||
if (!IsType&& file.Ext != ".md")
|
||||
{
|
||||
_message.Error("文件格式错误,请重新选择!");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@using AntSK.Domain.Domain.Dto
|
||||
@page "/Kms/DetailList/{KmsID}/{FileId}"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
|
||||
<Button Type="@ButtonType.Primary" OnClick="NavigateBack">返回</Button>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@using AntSK.Domain.Repositories
|
||||
@page "/KmsList"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
|
||||
<PageContainer Title="知识库列表">
|
||||
@@ -10,7 +12,7 @@
|
||||
<Search Placeholder="输入回车"
|
||||
EnterButton="@("搜索")"
|
||||
Size="large"
|
||||
Style="max-width: 522px; width: 100%;"
|
||||
Style="max-width: 522px; width: 100%;"
|
||||
OnSearch="Search" />
|
||||
</div>
|
||||
</Content>
|
||||
@@ -24,7 +26,7 @@
|
||||
@if (string.IsNullOrEmpty(context.Id))
|
||||
{
|
||||
<Button Type="dashed" class="newButton" @onclick="NavigateToAddKms">
|
||||
<Icon Type="plus" Theme="outline" /> 创建应用
|
||||
<Icon Type="plus" Theme="outline" /> 创建知识库
|
||||
</Button>
|
||||
}
|
||||
else
|
||||
@@ -43,8 +45,12 @@
|
||||
<DescriptionTemplate>
|
||||
<Paragraph class="item" Ellipsis>
|
||||
<!--todo: Ellipsis not working-->
|
||||
@context.Describe
|
||||
</Paragraph>
|
||||
<span>
|
||||
@context.Describe
|
||||
</span>
|
||||
<br />
|
||||
<span style="color:#c6c6c6">行切片:@context.MaxTokensPerLine 段落切片:@context.MaxTokensPerParagraph 重叠:@context.OverlappingTokens</span>
|
||||
</Paragraph>
|
||||
</DescriptionTemplate>
|
||||
</CardMeta>
|
||||
</Card>
|
||||
|
||||
52
AntSK/Pages/Setting/AIModel/AddModel.razor
Normal file
52
AntSK/Pages/Setting/AIModel/AddModel.razor
Normal file
@@ -0,0 +1,52 @@
|
||||
@namespace AntSK.Pages.Setting.AIModel
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@using AntSK.Domain.Model.Enum
|
||||
@page "/setting/model/add"
|
||||
@page "/setting/model/add/{ModelId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
|
||||
<PageContainer Title="新增模型">
|
||||
<ChildContent>
|
||||
<Card>
|
||||
<Form Model="@_aiModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="模型描述" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入模型描述" @bind-Value="@context.ModelDescription" />
|
||||
</FormItem>
|
||||
<FormItem Label="模型类型" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<RadioGroup @bind-Value="context.AIModelType">
|
||||
<Radio RadioButton Value="@(AIModelType.Chat)">会话模型</Radio>
|
||||
<Radio RadioButton Value="@(AIModelType.Embedding)">向量模型</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="模型地址" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入模型地址" @bind-Value="@context.EndPoint" />
|
||||
</FormItem>
|
||||
<FormItem Label="模型名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入模型名称" @bind-Value="@context.ModelName" />
|
||||
</FormItem>
|
||||
<FormItem Label="模型秘钥" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<InputPassword @bind-Value="@context.ModelKey" Placeholder="请输入模型秘钥" Size="@InputSize.Large" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" OnClick="HandleSubmit">
|
||||
保存
|
||||
</Button>
|
||||
<Button OnClick="Back">
|
||||
返回
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
68
AntSK/Pages/Setting/AIModel/AddModel.razor.cs
Normal file
68
AntSK/Pages/Setting/AIModel/AddModel.razor.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using AntDesign;
|
||||
using AntDesign.ProLayout;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using DocumentFormat.OpenXml.InkML;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
public partial class AddModel
|
||||
{
|
||||
[Parameter]
|
||||
public string ModelId { get; set; }
|
||||
[Inject] protected IAIModels_Repositories _aimodels_Repositories { get; set; }
|
||||
[Inject] protected MessageService? Message { get; set; }
|
||||
[Inject] public HttpClient HttpClient { get; set; }
|
||||
|
||||
private AIModels _aiModel = new AIModels();
|
||||
|
||||
IEnumerable<string> _menuKeys;
|
||||
|
||||
private List<MenuDataItem> menuList = new List<MenuDataItem>();
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (!string.IsNullOrEmpty(ModelId))
|
||||
{
|
||||
_aiModel= _aimodels_Repositories.GetFirst(p => p.Id == ModelId);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSubmit()
|
||||
{
|
||||
if (string.IsNullOrEmpty(ModelId))
|
||||
{
|
||||
//新增
|
||||
_aiModel.Id = Guid.NewGuid().ToString();
|
||||
|
||||
if (_aimodels_Repositories.IsAny(p => p.ModelDescription == _aiModel.ModelDescription ))
|
||||
{
|
||||
_ = Message.Error("模型描述已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_aimodels_Repositories.IsAny(p =>p.AIModelType==_aiModel.AIModelType&& p.EndPoint == _aiModel.EndPoint&&p.ModelKey==_aiModel.ModelKey&&p.ModelName==_aiModel.ModelName))
|
||||
{
|
||||
_ = Message.Error("模型已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
_aimodels_Repositories.Insert(_aiModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
_aimodels_Repositories.Update(_aiModel);
|
||||
}
|
||||
|
||||
Back();
|
||||
}
|
||||
|
||||
private void Back()
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/modellist");
|
||||
}
|
||||
}
|
||||
}
|
||||
15
AntSK/Pages/Setting/AIModel/ModelDown.razor
Normal file
15
AntSK/Pages/Setting/AIModel/ModelDown.razor
Normal file
@@ -0,0 +1,15 @@
|
||||
@namespace AntSK.Pages.Setting.AIModel
|
||||
@page "/setting/modeldown"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
<PageContainer Title="模型下载">
|
||||
<ChildContent>
|
||||
<h1>支持LLamaSharp的本地模型 支持gguf类型,推荐使用llama或者qwen</h1>
|
||||
<h1>如果模型加载报内存错误,可能是和llama.cpp版本不一致</h1>
|
||||
<a href="https://hf-mirror.com/models?search=gguf" target="_blank" rel="noopener noreferrer">打开下载地址</a>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
@code {
|
||||
|
||||
}
|
||||
79
AntSK/Pages/Setting/AIModel/ModelList.razor
Normal file
79
AntSK/Pages/Setting/AIModel/ModelList.razor
Normal file
@@ -0,0 +1,79 @@
|
||||
@namespace AntSK.Pages.Setting.AIModel
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Domain.Model.Enum
|
||||
@page "/setting/modellist"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<div>
|
||||
<PageContainer Title="模型管理">
|
||||
<ChildContent>
|
||||
<div class="standardList">
|
||||
|
||||
<Card Class="listCard"
|
||||
Title="模型列表"
|
||||
Style="margin-top: 24px;"
|
||||
BodyStyle="padding: 0 32px 40px 32px">
|
||||
<Extra>
|
||||
<div class="extraContent">
|
||||
|
||||
<Search Class="extraContentSearch" Placeholder="查询" @bind-Value="_searchKeyword" OnSearch="OnSearch" />
|
||||
</div>
|
||||
</Extra>
|
||||
<ChildContent>
|
||||
<Button Type="dashed"
|
||||
Style="width: 100%; margin-bottom: 8px;"
|
||||
OnClick="AddModel">
|
||||
<Icon Type="plus" Theme="outline" />
|
||||
新增模型
|
||||
</Button>
|
||||
|
||||
<AntList TItem="AIModels"
|
||||
DataSource="_data"
|
||||
ItemLayout="ListItemLayout.Horizontal">
|
||||
<ListItem Actions="new[] {
|
||||
edit(()=> Edit(context.Id)),
|
||||
delete(async ()=>await Delete(context.Id))
|
||||
}" Style="width:100%">
|
||||
<div class="listContent" style="width:100%">
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>模型描述</b>
|
||||
<p>@context.ModelDescription</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>模型类别</b>
|
||||
<p>
|
||||
@if (context.AIModelType == AIModelType.Chat)
|
||||
{
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">会话模型</Tag>
|
||||
}
|
||||
else if (context.AIModelType == AIModelType.Embedding)
|
||||
{
|
||||
<Tag Color="@PresetColor.Green.ToString()">向量模型</Tag>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>模型地址</b>
|
||||
<p>@context.EndPoint</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>模型名称</b>
|
||||
<p>@context.ModelName</p>
|
||||
</div>
|
||||
</div>
|
||||
</ListItem>
|
||||
</AntList>
|
||||
</ChildContent>
|
||||
</Card>
|
||||
</div>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
RenderFragment edit(Action clickAction) =>@<a key="edit" @onclick="@clickAction">修改</a>;
|
||||
RenderFragment delete(Action clickAction) =>@<a key="delete" @onclick="@clickAction">删除</a>;
|
||||
}
|
||||
67
AntSK/Pages/Setting/AIModel/ModelList.razor.cs
Normal file
67
AntSK/Pages/Setting/AIModel/ModelList.razor.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages.Setting.AIModel
|
||||
{
|
||||
public partial class ModelList
|
||||
{
|
||||
private readonly BasicListFormModel _model = new BasicListFormModel();
|
||||
|
||||
private List<AIModels> _data;
|
||||
|
||||
private string _searchKeyword;
|
||||
|
||||
[Inject]
|
||||
protected IAIModels_Repositories _aIModels_Repositories { get; set; }
|
||||
|
||||
[Inject]
|
||||
IConfirmService _confirmService { get; set; }
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
await InitData();
|
||||
}
|
||||
private async Task InitData(string searchKey=null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchKey))
|
||||
{
|
||||
_data = _aIModels_Repositories.GetList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_data = _aIModels_Repositories.GetList(p=>p.ModelName.Contains(searchKey)||p.ModelDescription.Contains(searchKey));
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
public async Task OnSearch() {
|
||||
await InitData(_searchKeyword);
|
||||
}
|
||||
|
||||
public async Task AddModel() {
|
||||
NavigationManager.NavigateTo("/setting/model/add");
|
||||
}
|
||||
|
||||
public void Edit(string modelid)
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/model/add/"+ modelid);
|
||||
}
|
||||
|
||||
public async Task Delete(string modelid)
|
||||
{
|
||||
var content = "是否确认删除此模型";
|
||||
var title = "删除";
|
||||
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
|
||||
if (result == ConfirmResult.Yes)
|
||||
{
|
||||
await _aIModels_Repositories.DeleteAsync(modelid);
|
||||
await InitData("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
186
AntSK/Pages/Setting/AIModel/ModelList.razor.css
Normal file
186
AntSK/Pages/Setting/AIModel/ModelList.razor.css
Normal file
@@ -0,0 +1,186 @@
|
||||
/* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable */
|
||||
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
|
||||
.standardList .ant-card-head {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.standardList .ant-card-head-title {
|
||||
padding: 24px 0;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.standardList .ant-card-extra {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.standardList .ant-list-pagination {
|
||||
margin-top: 24px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.standardList .ant-avatar-lg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > span {
|
||||
display: inline-block;
|
||||
margin-bottom: 4px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > p {
|
||||
margin: 0;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > em {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 56px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.standardList .listContent {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.standardList .listContent .listContentItem {
|
||||
display: inline-block;
|
||||
margin-left: 40px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.standardList .listContent .listContentItem > span {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.standardList .listContent .listContentItem > p {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.standardList .extraContentSearch {
|
||||
width: 272px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.standardList .ant-list-item-content {
|
||||
display: block;
|
||||
flex: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.standardList .ant-list-item-action {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .listContent {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .listContent > div {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .listCard .ant-card-head-title {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.standardList .extraContentSearch {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.standardList .headerInfo {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.standardList .headerInfo > em {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.standardList .listContent > div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.listCard .ant-radio-group {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) and (min-width: 768px) {
|
||||
.standardList .listContent > div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.standardList .listContent > div {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1400px) {
|
||||
.standardList .listContent {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.standardList .listContent > div:last-child {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.standardListForm .ant-form-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.standardListForm .ant-form-item:last-child {
|
||||
margin-bottom: 32px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.formResult {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.formResult [class^='title'] {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
60
AntSK/Pages/Setting/User/AddUser.razor
Normal file
60
AntSK/Pages/Setting/User/AddUser.razor
Normal file
@@ -0,0 +1,60 @@
|
||||
@namespace AntSK.Pages.Setting.User
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@page "/setting/user/add"
|
||||
@page "/setting/user/add/{UserId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
|
||||
<PageContainer Title="新增用户">
|
||||
<ChildContent>
|
||||
<Card>
|
||||
<Form Model="@_userModel"
|
||||
Style="margin-top: 8px;"
|
||||
OnFinish="HandleSubmit">
|
||||
<FormItem Label="工号" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入用户工号" @bind-Value="@context.No" />
|
||||
</FormItem>
|
||||
<FormItem Label="用户名称" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入用户名称" @bind-Value="@context.Name" />
|
||||
</FormItem>
|
||||
<FormItem Label="用户密码" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Type="password" Placeholder="请输入用户密码" @bind-Value="@context.Password" />
|
||||
</FormItem>
|
||||
<FormItem Label="用户备注" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Input Placeholder="请输入用户备注" @bind-Value="@context.Describe" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="菜单权限" LabelCol="LayoutModel._formItemLayout.LabelCol" WrapperCol="LayoutModel._formItemLayout.WrapperCol">
|
||||
<Select Mode="multiple"
|
||||
@bind-Values="_menuKeys"
|
||||
Placeholder="选择菜单权限"
|
||||
TItemValue="string"
|
||||
TItem="string"
|
||||
Size="@AntSizeLDSType.Default">
|
||||
<SelectOptions>
|
||||
@foreach (var menu in menuList)
|
||||
{
|
||||
<SelectOption TItem="string" TItemValue="string" Value="@menu.Key" Label="@menu.Name" />
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" OnClick="HandleSubmit">
|
||||
保存
|
||||
</Button>
|
||||
<Button OnClick="Back">
|
||||
返回
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
76
AntSK/Pages/Setting/User/AddUser.razor.cs
Normal file
76
AntSK/Pages/Setting/User/AddUser.razor.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using AntDesign;
|
||||
using AntDesign.ProLayout;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using DocumentFormat.OpenXml.InkML;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages.Setting.User
|
||||
{
|
||||
public partial class AddUser
|
||||
{
|
||||
[Parameter]
|
||||
public string UserId { get; set; }
|
||||
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
|
||||
[Inject] protected MessageService? Message { get; set; }
|
||||
[Inject] public HttpClient HttpClient { get; set; }
|
||||
|
||||
private Users _userModel = new Users();
|
||||
private string _password = "";
|
||||
IEnumerable<string> _menuKeys;
|
||||
|
||||
private List<MenuDataItem> menuList = new List<MenuDataItem>();
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (!string.IsNullOrEmpty(UserId))
|
||||
{
|
||||
_userModel= _users_Repositories.GetFirst(p => p.Id == UserId);
|
||||
_password= _userModel.Password;
|
||||
}
|
||||
menuList = (await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json")).ToList().Where(p=>p.Key!= "setting").ToList();
|
||||
_menuKeys= _userModel.MenuRole?.Split(",");
|
||||
}
|
||||
|
||||
private void HandleSubmit()
|
||||
{
|
||||
_userModel.MenuRole = string.Join(",", _menuKeys);
|
||||
if (string.IsNullOrEmpty(UserId))
|
||||
{
|
||||
//新增
|
||||
_userModel.Id = Guid.NewGuid().ToString();
|
||||
|
||||
if (_userModel.No == LoginOption.User)
|
||||
{
|
||||
_ = Message.Error("工号不能为管理员账号!", 2);
|
||||
return;
|
||||
}
|
||||
if (_users_Repositories.IsAny(p => p.No == _userModel.No))
|
||||
{
|
||||
_ = Message.Error("工号已存在!", 2);
|
||||
return;
|
||||
}
|
||||
_userModel.Password=PasswordUtil.HashPassword(_userModel.Password);
|
||||
_users_Repositories.Insert(_userModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
//修改
|
||||
if (_userModel.Password!=_password)
|
||||
{
|
||||
_userModel.Password = PasswordUtil.HashPassword(_userModel.Password);
|
||||
}
|
||||
_users_Repositories.Update(_userModel);
|
||||
}
|
||||
|
||||
Back();
|
||||
}
|
||||
|
||||
private void Back()
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/userlist");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user