mirror of
https://github.com/AIDotNet/AntSK.git
synced 2026-02-21 17:09:20 +08:00
Compare commits
107 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 |
@@ -5,19 +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.SemanticKernel.Plugins.Core" Version="1.3.0-alpha" />
|
||||
<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,11 +167,31 @@
|
||||
描述
|
||||
</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列表
|
||||
@@ -147,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
|
||||
@@ -404,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
|
||||
@@ -494,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; }
|
||||
}
|
||||
}
|
||||
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,16 +25,34 @@ 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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\Setting\" />
|
||||
<None Update="plugins\KMSPlugin\Ask\skprompt.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -16,6 +16,19 @@
|
||||
</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>
|
||||
对外接口
|
||||
@@ -89,18 +102,28 @@
|
||||
<param name="fileid"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendKms(System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<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.SendChat(System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendKms(System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送普通对话
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(BasicLayout)">
|
||||
<p>Sorry, there's nothing at this address.</p>
|
||||
<AntSK.Pages.Exception._404/>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
@@ -29,4 +29,4 @@
|
||||
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");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,10 @@ namespace AntSK.Controllers
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("api/v1/chat/completions")]
|
||||
public async Task<IActionResult> chat(OpenAIModel model)
|
||||
public async Task chat(OpenAIModel model)
|
||||
{
|
||||
string sk = HttpContext.Request.Headers["Authorization"].ConvertToString();
|
||||
var result=await _openApiService.Chat(model,sk);
|
||||
return Ok(result);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,13 +9,20 @@ 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 role { get; set; }
|
||||
|
||||
public string content { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIEmbeddingModel
|
||||
{
|
||||
public List<string> input { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AntSK.Models.OpenAPI
|
||||
{
|
||||
public class OpenAIResult
|
||||
{
|
||||
public string id { get; set; } = Guid.NewGuid().ToString();
|
||||
[JsonPropertyName("object")]
|
||||
[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";
|
||||
@@ -19,5 +21,47 @@ namespace AntSK.Models.OpenAPI
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -12,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)
|
||||
{
|
||||
@@ -53,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()
|
||||
{
|
||||
@@ -72,7 +60,7 @@ namespace AntSK.Pages.AppPage
|
||||
_appModel.SecretKey="sk-"+ Guid.NewGuid().ToString();
|
||||
if (_apps_Repositories.IsAny(p => p.Name == _appModel.Name))
|
||||
{
|
||||
_errorMsg = "名称已存在!";
|
||||
_ = Message.Error("名称已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,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 {
|
||||
@@ -89,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,4 +1,4 @@
|
||||
@namespace AntSK.Pages
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@page "/AppList"
|
||||
@inject NavigationManager NavigationManager
|
||||
@@ -20,7 +20,7 @@
|
||||
<AntList TItem="Apps"
|
||||
DataSource="_data"
|
||||
ItemLayout="ListItemLayout.Horizontal"
|
||||
Grid="_listGridType">
|
||||
Grid="LayoutModel._listGridType">
|
||||
<ListItem NoFlex>
|
||||
@if (string.IsNullOrEmpty(context.Id))
|
||||
{
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<GridRow Gutter="(16, 16)">
|
||||
<GridCol Span="12">
|
||||
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
|
||||
<Card Style="height:800px;overflow: auto;">
|
||||
<Card Style="height:700px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="setting" /> 选择应用
|
||||
<Select DataSource="@_list"
|
||||
@@ -23,28 +23,36 @@
|
||||
</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>
|
||||
<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>
|
||||
@@ -59,7 +67,7 @@
|
||||
</Spin>
|
||||
</GridCol>
|
||||
<GridCol Span="12">
|
||||
<Card Style="height: 800px;overflow: auto;">
|
||||
<Card Style="height: 700px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="search" /> 调试结果
|
||||
</TitleTemplate>
|
||||
@@ -67,8 +75,8 @@
|
||||
|
||||
</Extra>
|
||||
<Body>
|
||||
<AntList Bordered DataSource="@RelevantSources">
|
||||
<ChildContent Context="item">
|
||||
<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))
|
||||
@@ -80,7 +88,65 @@
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
|
||||
<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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
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;
|
||||
@@ -13,6 +15,7 @@ using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Text;
|
||||
@@ -23,18 +26,22 @@ 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 = [];
|
||||
@@ -48,33 +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);
|
||||
}
|
||||
|
||||
Sendding = true;
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
Sendding = false;
|
||||
|
||||
}
|
||||
protected async Task OnCopyAsync(MessageInfo item)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_messageInput = item.Questions;
|
||||
_messageInput = item.Context;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,14 +111,14 @@ namespace AntSK.Pages.ChatPage
|
||||
|
||||
protected async Task<bool> SendAsync(string questions)
|
||||
{
|
||||
string msg = questions;
|
||||
string msg = "";
|
||||
//处理多轮会话
|
||||
if (MessageList.Count > 0)
|
||||
{
|
||||
msg = await HistorySummarize(questions);
|
||||
}
|
||||
|
||||
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
switch (app.Type)
|
||||
{
|
||||
case "chat":
|
||||
@@ -120,6 +143,7 @@ namespace AntSK.Pages.ChatPage
|
||||
/// <returns></returns>
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
@@ -128,40 +152,59 @@ namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
|
||||
string dataMsg = "";
|
||||
if (xlresult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
foreach (var item in xlresult.Results)
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info1 = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Questions = questions,
|
||||
Answers = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info1);
|
||||
}
|
||||
|
||||
foreach (var x in kmsResult.RelevantSources)
|
||||
{
|
||||
foreach (var xsd in x.Partitions)
|
||||
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 = x.SourceName;
|
||||
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == x.SourceName);
|
||||
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(xsd.Text), Relevance = xsd.Relevance });
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,16 +217,21 @@ namespace AntSK.Pages.ChatPage
|
||||
/// <returns></returns>
|
||||
private async Task SendChat(string questions, string msg, Apps app)
|
||||
{
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
if (string.IsNullOrEmpty(app.Prompt)||!app.Prompt.Contains("{{$input}}"))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$input}}";
|
||||
app.Prompt = app.Prompt.ConvertToString()+"{{$input}}";
|
||||
}
|
||||
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 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();
|
||||
@@ -193,9 +241,8 @@ namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Questions = questions;
|
||||
info.Answers = content.Content!;
|
||||
info.HtmlAnswers = content.Content!;
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
@@ -208,10 +255,16 @@ namespace AntSK.Pages.ChatPage
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
@@ -219,18 +272,37 @@ namespace AntSK.Pages.ChatPage
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(string questions)
|
||||
{
|
||||
StringBuilder history = new StringBuilder();
|
||||
foreach (var item in MessageList)
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
if (MessageList.Count > 1)
|
||||
{
|
||||
history.Append($"user:{item.Questions}{Environment.NewLine}");
|
||||
history.Append($"assistant:{item.Answers}{Environment.NewLine}");
|
||||
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;
|
||||
}
|
||||
|
||||
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 = $"历史对话:{his}{Environment.NewLine} 用户问题:{Environment.NewLine}{questions}"; ;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,27 +8,36 @@
|
||||
<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.OrderByDescending(o => o.CreateTime).ToList())" Context="item">
|
||||
<GridCol Span="24">
|
||||
<Card>
|
||||
<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 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;">
|
||||
@@ -40,6 +49,66 @@
|
||||
</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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ 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
|
||||
{
|
||||
@@ -34,7 +37,7 @@ namespace AntSK.Pages.ChatPage
|
||||
[Inject]
|
||||
protected MemoryServerless _memory { get; set; }
|
||||
[Inject]
|
||||
protected Kernel _kernel { get; set; }
|
||||
protected IKernelService _kernelService { get; set; }
|
||||
|
||||
protected bool _loading = false;
|
||||
protected List<MessageInfo> MessageList = [];
|
||||
@@ -43,6 +46,7 @@ namespace AntSK.Pages.ChatPage
|
||||
protected bool Sendding = false;
|
||||
|
||||
protected Apps app = new Apps();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
@@ -50,23 +54,40 @@ namespace AntSK.Pages.ChatPage
|
||||
}
|
||||
protected async Task OnSendAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_messageInput))
|
||||
try
|
||||
{
|
||||
_ = Message.Info("请输入消息", 2);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_messageInput))
|
||||
{
|
||||
_ = Message.Info("请输入消息", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
Sendding = true;
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
Sendding = false;
|
||||
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.Questions;
|
||||
_messageInput = item.Context;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,7 +101,7 @@ namespace AntSK.Pages.ChatPage
|
||||
|
||||
protected async Task<bool> SendAsync(string questions)
|
||||
{
|
||||
string msg = questions;
|
||||
string msg = "";
|
||||
//处理多轮会话
|
||||
if (MessageList.Count > 0)
|
||||
{
|
||||
@@ -112,6 +133,7 @@ namespace AntSK.Pages.ChatPage
|
||||
/// <returns></returns>
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
@@ -121,24 +143,50 @@ namespace AntSK.Pages.ChatPage
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
|
||||
var xlresult = await _memory.SearchAsync(questions, index: "kms", filters: filters);
|
||||
string dataMsg = "";
|
||||
if (xlresult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
foreach (var item in xlresult.Results)
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info1 = new MessageInfo()
|
||||
foreach (var part in item.Partitions)
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Questions = questions,
|
||||
Answers = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info1);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,16 +199,22 @@ namespace AntSK.Pages.ChatPage
|
||||
/// <returns></returns>
|
||||
private async Task SendChat(string questions, string msg, Apps app)
|
||||
{
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
var _kernel= _kernelService.GetKernel();
|
||||
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$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 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 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();
|
||||
@@ -170,9 +224,8 @@ namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Questions = questions;
|
||||
info.Answers = content.Content!;
|
||||
info.HtmlAnswers = content.Content!;
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
@@ -196,18 +249,37 @@ namespace AntSK.Pages.ChatPage
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(string questions)
|
||||
{
|
||||
StringBuilder history = new StringBuilder();
|
||||
foreach (var item in MessageList)
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
if (MessageList.Count > 1)
|
||||
{
|
||||
history.Append($"user:{item.Questions}{Environment.NewLine}");
|
||||
history.Append($"assistant:{item.Answers}{Environment.NewLine}");
|
||||
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;
|
||||
}
|
||||
|
||||
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 = $"历史对话:{his}{Environment.NewLine} 用户问题:{Environment.NewLine}{questions}"; ;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,19 @@
|
||||
|
||||
<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站账号,获取更多精彩内容。
|
||||
@@ -23,7 +33,9 @@
|
||||
|
||||
发送“进群请求”,我将会把您邀请进群。
|
||||
|
||||
期待与您在线上交流技术,共同进步!");
|
||||
期待与您在线上交流技术,共同进步!
|
||||
|
||||
也欢迎您关注我的公众号,后面可以第一时间知道**AntSK**项目进展。");
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,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>
|
||||
@@ -29,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">
|
||||
@@ -50,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>
|
||||
@@ -66,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">
|
||||
@@ -102,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"
|
||||
@@ -120,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("文件格式错误,请重新选择!");
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Search Placeholder="输入回车"
|
||||
EnterButton="@("搜索")"
|
||||
Size="large"
|
||||
Style="max-width: 522px; width: 100%;"
|
||||
Style="max-width: 522px; width: 100%;"
|
||||
OnSearch="Search" />
|
||||
</div>
|
||||
</Content>
|
||||
@@ -26,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
|
||||
@@ -45,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
44
AntSK/Pages/Setting/User/UserInfo.razor
Normal file
44
AntSK/Pages/Setting/User/UserInfo.razor
Normal file
@@ -0,0 +1,44 @@
|
||||
@namespace AntSK.Pages.Setting.User
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@page "/setting/user/info/{UserNo}"
|
||||
@inject IMessageService _message
|
||||
@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" Disabled="true" />
|
||||
</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=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" HtmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
<Button OnClick="Back">
|
||||
返回
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
</PageContainer>
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
49
AntSK/Pages/Setting/User/UserInfo.razor.cs
Normal file
49
AntSK/Pages/Setting/User/UserInfo.razor.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace AntSK.Pages.Setting.User
|
||||
{
|
||||
public partial class UserInfo
|
||||
{
|
||||
[Parameter]
|
||||
public string UserNo { get; set; }
|
||||
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
|
||||
[Inject] protected MessageService? Message { get; set; }
|
||||
|
||||
private Users _userModel = new Users();
|
||||
private string _password = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (!string.IsNullOrEmpty(UserNo))
|
||||
{
|
||||
_userModel= _users_Repositories.GetFirst(p => p.No == UserNo);
|
||||
_password= _userModel.Password;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleSubmit()
|
||||
{
|
||||
|
||||
//修改
|
||||
if (_userModel.Password!=_password)
|
||||
{
|
||||
_userModel.Password = PasswordUtil.HashPassword(_userModel.Password);
|
||||
}
|
||||
_users_Repositories.Update(_userModel);
|
||||
|
||||
|
||||
_ = Message.Info("保存成功!", 2);
|
||||
}
|
||||
|
||||
private async Task Back()
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
66
AntSK/Pages/Setting/User/UserList.razor
Normal file
66
AntSK/Pages/Setting/User/UserList.razor
Normal file
@@ -0,0 +1,66 @@
|
||||
@namespace AntSK.Pages.Setting.User
|
||||
@using AntSK.Domain.Repositories
|
||||
@page "/setting/userlist"
|
||||
@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="AddUser">
|
||||
<Icon Type="plus" Theme="outline" />
|
||||
新增用户
|
||||
</Button>
|
||||
|
||||
<AntList TItem="Users"
|
||||
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.No</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>姓名</b>
|
||||
<p>@context.Name</p>
|
||||
</div>
|
||||
<div class="listContentItem" style="width:20%">
|
||||
<b>备注</b>
|
||||
<p>@context.Describe</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>;
|
||||
|
||||
}
|
||||
64
AntSK/Pages/Setting/User/UserList.razor.cs
Normal file
64
AntSK/Pages/Setting/User/UserList.razor.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages.Setting.User
|
||||
{
|
||||
public partial class UserList
|
||||
{
|
||||
private readonly BasicListFormModel _model = new BasicListFormModel();
|
||||
|
||||
private List<Users> _data;
|
||||
|
||||
private string _searchKeyword;
|
||||
|
||||
[Inject]
|
||||
protected IUsers_Repositories _users_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 = _users_Repositories.GetList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_data = _users_Repositories.GetList(p=>p.Name.Contains(searchKey)||p.Describe.Contains(searchKey)||p.No.Contains(searchKey));
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
public async Task OnSearch() {
|
||||
await InitData(_searchKeyword);
|
||||
}
|
||||
|
||||
public async Task AddUser() {
|
||||
NavigationManager.NavigateTo("/setting/user/add");
|
||||
}
|
||||
|
||||
public void Edit(string userid)
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/user/add/"+userid);
|
||||
}
|
||||
|
||||
public async Task Delete(string modelid)
|
||||
{
|
||||
var content = "是否确认删除此用户";
|
||||
var title = "删除";
|
||||
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
|
||||
if (result == ConfirmResult.Yes)
|
||||
{
|
||||
await _users_Repositories.DeleteAsync(modelid);
|
||||
await InitData("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
186
AntSK/Pages/Setting/User/UserList.razor.css
Normal file
186
AntSK/Pages/Setting/User/UserList.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;
|
||||
}
|
||||
@@ -6,6 +6,8 @@ using AntSK.Services;
|
||||
using AntSK.Domain.Options;
|
||||
using SqlSugar;
|
||||
using AntSK.Services.Auth;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
|
||||
namespace AntSK.Pages.User
|
||||
{
|
||||
@@ -15,12 +17,11 @@ namespace AntSK.Pages.User
|
||||
|
||||
[Inject] public NavigationManager NavigationManager { get; set; }
|
||||
|
||||
[Inject] public IAccountService AccountService { get; set; }
|
||||
|
||||
[Inject] public MessageService Message { get; set; }
|
||||
|
||||
public async Task HandleSubmit()
|
||||
{
|
||||
//判断是否管理员
|
||||
var loginFailed = await((AntSKAuthProvider)AuthenticationStateProvider).SignIn(_model.UserName, _model.Password);
|
||||
if (loginFailed)
|
||||
{
|
||||
|
||||
@@ -75,6 +75,7 @@ builder.Services.AddSwaggerGen(c =>
|
||||
builder.Configuration.GetSection("ConnectionStrings").Get<ConnectionOption>();
|
||||
builder.Configuration.GetSection("OpenAIOption").Get<OpenAIOption>();
|
||||
builder.Configuration.GetSection("Login").Get<LoginOption>();
|
||||
builder.Configuration.GetSection("LLamaSharp").Get<LLamaSharpOption>();
|
||||
}
|
||||
InitSK(builder);
|
||||
var app = builder.Build();
|
||||
@@ -87,7 +88,7 @@ if (!app.Environment.IsDevelopment())
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
//app.UseHttpsRedirection();
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
@@ -118,21 +119,28 @@ void InitDB(WebApplication app)
|
||||
_repository.GetDB().CodeFirst.InitTables(typeof(Apps));
|
||||
_repository.GetDB().CodeFirst.InitTables(typeof(Kmss));
|
||||
_repository.GetDB().CodeFirst.InitTables(typeof(KmsDetails));
|
||||
_repository.GetDB().CodeFirst.InitTables(typeof(Users));
|
||||
_repository.GetDB().CodeFirst.InitTables(typeof(Apis));
|
||||
_repository.GetDB().CodeFirst.InitTables(typeof(AIModels));
|
||||
//创建vector插件如果数据库没有则需要提供支持向量的数据库
|
||||
_repository.GetDB().Ado.ExecuteCommandAsync($"CREATE EXTENSION IF NOT EXISTS vector;");
|
||||
}
|
||||
}
|
||||
|
||||
//初始化SK
|
||||
void InitSK(WebApplicationBuilder builder)
|
||||
{
|
||||
{
|
||||
var services = builder.Services;
|
||||
var handler = new OpenAIHttpClientHandler();
|
||||
var httpClient = new HttpClient(handler);
|
||||
httpClient.Timeout= TimeSpan.FromMinutes(5);
|
||||
services.AddScoped<Kernel>((serviceProvider) =>
|
||||
{
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: OpenAIOption.Model,
|
||||
apiKey: OpenAIOption.Key,
|
||||
httpClient: new HttpClient(handler))
|
||||
httpClient: httpClient)
|
||||
.Build();
|
||||
RegisterPluginsWithKernel(kernel);
|
||||
return kernel;
|
||||
@@ -140,31 +148,40 @@ void InitSK(WebApplicationBuilder builder)
|
||||
//Kernel Memory
|
||||
var searchClientConfig = new SearchClientConfig
|
||||
{
|
||||
MaxAskPromptSize = 128000,
|
||||
MaxAskPromptSize = 2048,
|
||||
MaxMatchesCount = 3,
|
||||
AnswerTokens = 1000,
|
||||
EmptyAnswer = "知识库未搜索到相关内容"
|
||||
};
|
||||
|
||||
builder.Services.Configure<PostgresConfig>(builder.Configuration.GetSection("Postgres"));
|
||||
var postgresConfig = builder.Configuration.GetSection("Postgres").Get<PostgresConfig>()!;
|
||||
|
||||
services.AddScoped<MemoryServerless>(serviceProvider =>
|
||||
{
|
||||
var memory = new KernelMemoryBuilder()
|
||||
.WithPostgresMemoryDb(postgresConfig)
|
||||
.WithSimpleFileStorage(new SimpleFileStorageConfig { StorageType = FileSystemTypes.Volatile, Directory = "_files" })
|
||||
.WithSearchClientConfig(searchClientConfig)
|
||||
//如果用本地模型需要设置token小一点。
|
||||
.WithCustomTextPartitioningOptions(new Microsoft.KernelMemory.Configuration.TextPartitioningOptions
|
||||
{
|
||||
MaxTokensPerLine = 99,
|
||||
MaxTokensPerParagraph = 299,
|
||||
OverlappingTokens = 47
|
||||
})
|
||||
.WithOpenAITextGeneration(new OpenAIConfig()
|
||||
{
|
||||
APIKey = OpenAIOption.Key,
|
||||
TextModel = OpenAIOption.Model
|
||||
|
||||
}, null, new HttpClient(handler))
|
||||
}, null, httpClient)
|
||||
.WithOpenAITextEmbeddingGeneration(new OpenAIConfig()
|
||||
{
|
||||
APIKey = OpenAIOption.Key,
|
||||
EmbeddingModel = OpenAIOption.EmbeddingModel
|
||||
|
||||
}, null, false, new HttpClient(handler))
|
||||
}, null, false, httpClient)
|
||||
.Build<MemoryServerless>();
|
||||
return memory;
|
||||
});
|
||||
|
||||
@@ -1,29 +1,52 @@
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace AntSK.Services.Auth
|
||||
{
|
||||
public class AntSKAuthProvider : AuthenticationStateProvider
|
||||
public class AntSKAuthProvider(IUsers_Repositories _users_Repositories) : AuthenticationStateProvider
|
||||
{
|
||||
private ClaimsIdentity identity = new ClaimsIdentity();
|
||||
|
||||
|
||||
public async Task<bool> SignIn(string username, string password)
|
||||
{
|
||||
|
||||
var user = _users_Repositories.GetFirst(p => p.No == username);
|
||||
if (username == LoginOption.User && password == LoginOption.Password)
|
||||
{
|
||||
// 用户认证成功,创建用户的ClaimsIdentity
|
||||
// 管理员认证成功,创建用户的ClaimsIdentity
|
||||
var claims = new[] { new Claim(ClaimTypes.Name, username) };
|
||||
identity = new ClaimsIdentity(claims, "AntSK");
|
||||
identity = new ClaimsIdentity(claims, "AntSKAdmin");
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// 用户认证失败
|
||||
return false;
|
||||
if (user.IsNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!PasswordUtil.VerifyPassword(password, user.Password))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 用户认证成功,创建用户的ClaimsIdentity
|
||||
var claims = new[] { new Claim(ClaimTypes.Name, username) };
|
||||
identity = new ClaimsIdentity(claims, "AntSKUser");
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public ClaimsPrincipal GetCurrentUser()
|
||||
{
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
return user;
|
||||
}
|
||||
public override Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
|
||||
106
AntSK/Services/LLamaSharp/LLamaChatService.cs
Normal file
106
AntSK/Services/LLamaSharp/LLamaChatService.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Models.OpenAPI;
|
||||
using AntSK.Models;
|
||||
using LLama;
|
||||
using LLama.Common;
|
||||
using Newtonsoft.Json;
|
||||
using static Azure.Core.HttpHeader;
|
||||
|
||||
namespace AntSK.Services.LLamaSharp
|
||||
{
|
||||
public interface ILLamaChatService
|
||||
{
|
||||
Task<string> ChatAsync(string input);
|
||||
IAsyncEnumerable<string> ChatStreamAsync(string input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[ServiceDescription(typeof(ILLamaChatService), Domain.Common.DependencyInjection.ServiceLifetime.Singleton)]
|
||||
public class LLamaChatService : IDisposable, ILLamaChatService
|
||||
{
|
||||
private readonly ChatSession _session;
|
||||
private readonly LLamaContext _context;
|
||||
private readonly ILogger<LLamaChatService> _logger;
|
||||
private bool _continue = false;
|
||||
|
||||
private const string SystemPrompt = "You are a personal assistant who needs to help users in Chinese.";
|
||||
|
||||
public LLamaChatService(ILogger<LLamaChatService> logger)
|
||||
{
|
||||
var @params = new ModelParams(LLamaSharpOption.Chat)
|
||||
{
|
||||
ContextSize = 2048,
|
||||
};
|
||||
|
||||
// todo: share weights from a central service
|
||||
using var weights = LLamaWeights.LoadFromFile(@params);
|
||||
|
||||
_logger = logger;
|
||||
_context = new LLamaContext(weights, @params);
|
||||
|
||||
_session = new ChatSession(new InteractiveExecutor(_context));
|
||||
_session.History.AddMessage(AuthorRole.System, SystemPrompt);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<string> ChatAsync(string input)
|
||||
{
|
||||
|
||||
if (!_continue)
|
||||
{
|
||||
_logger.LogInformation("Prompt: {text}", SystemPrompt);
|
||||
_continue = true;
|
||||
}
|
||||
_logger.LogInformation("Input: {text}", input);
|
||||
var outputs = _session.ChatAsync(
|
||||
new ChatHistory.Message(AuthorRole.User, input),
|
||||
new InferenceParams()
|
||||
{
|
||||
RepeatPenalty = 1.0f,
|
||||
AntiPrompts = new string[] { "User:" },
|
||||
});
|
||||
|
||||
var result = "";
|
||||
await foreach (var output in outputs)
|
||||
{
|
||||
_logger.LogInformation("Message: {output}", output);
|
||||
result += output;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> ChatStreamAsync(string input)
|
||||
{
|
||||
if (!_continue)
|
||||
{
|
||||
_logger.LogInformation(SystemPrompt);
|
||||
_continue = true;
|
||||
}
|
||||
|
||||
_logger.LogInformation(input);
|
||||
|
||||
var outputs = _session.ChatAsync(
|
||||
new ChatHistory.Message(AuthorRole.User, input!)
|
||||
, new InferenceParams()
|
||||
{
|
||||
RepeatPenalty = 1.0f,
|
||||
AntiPrompts = new string[] { "User:" },
|
||||
});
|
||||
|
||||
await foreach (var output in outputs)
|
||||
{
|
||||
_logger.LogInformation(output);
|
||||
yield return output;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
42
AntSK/Services/LLamaSharp/LLamaEmbeddingService.cs
Normal file
42
AntSK/Services/LLamaSharp/LLamaEmbeddingService.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Models.OpenAPI;
|
||||
using AntSK.Models;
|
||||
using LLama;
|
||||
using LLama.Common;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AntSK.Services.LLamaSharp
|
||||
{
|
||||
public interface ILLamaEmbeddingService
|
||||
{
|
||||
Task<List<float>> Embedding(string text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地Embedding
|
||||
/// </summary>
|
||||
[ServiceDescription(typeof(ILLamaEmbeddingService), Domain.Common.DependencyInjection.ServiceLifetime.Singleton)]
|
||||
public class LLamaEmbeddingService : IDisposable, ILLamaEmbeddingService
|
||||
{
|
||||
private LLamaEmbedder _embedder;
|
||||
|
||||
public LLamaEmbeddingService() {
|
||||
|
||||
var @params = new ModelParams(LLamaSharpOption.Embedding) { EmbeddingMode = true };
|
||||
using var weights = LLamaWeights.LoadFromFile(@params);
|
||||
_embedder = new LLamaEmbedder(weights, @params);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_embedder?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<List<float>> Embedding(string text)
|
||||
{
|
||||
float[] embeddings =await _embedder.GetEmbeddings(text);
|
||||
//PG只有1536维
|
||||
return embeddings.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
79
AntSK/Services/LLamaSharp/LLamaSharpService.cs
Normal file
79
AntSK/Services/LLamaSharp/LLamaSharpService.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Domain.Service;
|
||||
using AntSK.Domain.Options;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Models;
|
||||
using AntSK.Models.OpenAPI;
|
||||
using AntSK.Services.OpenApi;
|
||||
using Azure;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using LLama;
|
||||
using LLama.Common;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using static Azure.Core.HttpHeader;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services.LLamaSharp
|
||||
{
|
||||
|
||||
public interface ILLamaSharpService
|
||||
{
|
||||
Task Chat(OpenAIModel model, HttpContext HttpContext);
|
||||
Task ChatStream(OpenAIModel model, HttpContext HttpContext);
|
||||
Task Embedding(OpenAIEmbeddingModel model, HttpContext HttpContext);
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(ILLamaSharpService), ServiceLifetime.Scoped)]
|
||||
public class LLamaSharpService(
|
||||
ILLamaEmbeddingService _lLamaEmbeddingService,
|
||||
ILLamaChatService _lLamaChatService
|
||||
) : ILLamaSharpService
|
||||
{
|
||||
|
||||
public async Task ChatStream(OpenAIModel model, HttpContext HttpContext)
|
||||
{
|
||||
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
|
||||
OpenAIStreamResult result = new OpenAIStreamResult();
|
||||
result.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
result.choices = new List<StreamChoicesModel>() { new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
|
||||
string questions = model.messages.LastOrDefault().content;
|
||||
|
||||
await foreach (var r in _lLamaChatService.ChatStreamAsync(questions))
|
||||
{
|
||||
result.choices[0].delta.content = r.ConvertToString();
|
||||
string message = $"data: {JsonConvert.SerializeObject(result)}\n\n";
|
||||
await HttpContext.Response.WriteAsync(message, Encoding.UTF8);
|
||||
await HttpContext.Response.Body.FlushAsync();
|
||||
}
|
||||
await HttpContext.Response.WriteAsync("data: [DONE]");
|
||||
await HttpContext.Response.Body.FlushAsync();
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
|
||||
public async Task Chat(OpenAIModel model, HttpContext HttpContext)
|
||||
{
|
||||
string questions = model.messages.LastOrDefault().content;
|
||||
OpenAIResult result = new OpenAIResult();
|
||||
result.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
result.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
|
||||
|
||||
result.choices[0].message.content =await _lLamaChatService.ChatAsync(questions); ;
|
||||
HttpContext.Response.ContentType = "application/json";
|
||||
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result));
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task Embedding(OpenAIEmbeddingModel model, HttpContext HttpContext)
|
||||
{
|
||||
var result = new OpenAIEmbeddingResult();
|
||||
result.data[0].embedding = await _lLamaEmbeddingService.Embedding(model.input[0]);
|
||||
HttpContext.Response.ContentType = "application/json";
|
||||
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result));
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,21 @@ using System;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
using AntDesign.Core.Extensions;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure;
|
||||
using Azure.Core;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using AntDesign;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using AntSK.Domain.Domain.Interface;
|
||||
using static LLama.Common.ChatHistory;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
|
||||
namespace AntSK.Services.OpenApi
|
||||
{
|
||||
public interface IOpenApiService
|
||||
{
|
||||
Task<OpenAIResult> Chat(OpenAIModel model, string sk);
|
||||
Task Chat(OpenAIModel model, string sk, HttpContext HttpContext);
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IOpenApiService), ServiceLifetime.Scoped)]
|
||||
@@ -27,19 +36,13 @@ namespace AntSK.Services.OpenApi
|
||||
IApps_Repositories _apps_Repositories,
|
||||
IKmss_Repositories _kmss_Repositories,
|
||||
IKmsDetails_Repositories _kmsDetails_Repositories,
|
||||
Kernel _kernel,
|
||||
MemoryServerless _memory
|
||||
MemoryServerless _memory,
|
||||
IKernelService _kernelService
|
||||
) : IOpenApiService
|
||||
{
|
||||
public async Task<OpenAIResult> Chat(OpenAIModel model,string sk)
|
||||
public async Task Chat(OpenAIModel model,string sk, HttpContext HttpContext)
|
||||
{
|
||||
OpenAIResult result = new OpenAIResult();
|
||||
result.created= DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
result.choices=new List<ChoicesModel>() { new ChoicesModel() { message=new OpenAIMessage() { role= "assistant" } } };
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == sk);
|
||||
|
||||
|
||||
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == sk);
|
||||
if (app.IsNotNull())
|
||||
{
|
||||
string msg= await HistorySummarize(model);
|
||||
@@ -47,46 +50,84 @@ namespace AntSK.Services.OpenApi
|
||||
{
|
||||
case "chat":
|
||||
//普通会话
|
||||
result.choices[0].message.content= await SendChat( msg, app);
|
||||
if (model.stream)
|
||||
{
|
||||
OpenAIStreamResult result1 = new OpenAIStreamResult();
|
||||
result1.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
result1.choices = new List<StreamChoicesModel>() { new StreamChoicesModel() { delta = new OpenAIMessage() { role = "assistant" } } };
|
||||
await SendChatStream( HttpContext, result1, app, msg);
|
||||
HttpContext.Response.ContentType = "application/json";
|
||||
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result1));
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenAIResult result2 = new OpenAIResult();
|
||||
result2.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
result2.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
|
||||
result2.choices[0].message.content = await SendChat(msg, app);
|
||||
HttpContext.Response.ContentType = "application/json";
|
||||
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result2));
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
break;
|
||||
case "kms":
|
||||
//知识库问答
|
||||
result.choices[0].message.content = await SendKms( msg, app);
|
||||
OpenAIResult result3 = new OpenAIResult();
|
||||
result3.created = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
result3.choices = new List<ChoicesModel>() { new ChoicesModel() { message = new OpenAIMessage() { role = "assistant" } } };
|
||||
result3.choices[0].message.content = await SendKms( msg, app);
|
||||
HttpContext.Response.ContentType = "application/json";
|
||||
await HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(result3));
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> SendKms( string msg, Apps app)
|
||||
private async Task SendChatStream( HttpContext HttpContext, OpenAIStreamResult result, Apps app, string msg)
|
||||
{
|
||||
string result = "";
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
var temperature = app.Temperature / 100;//存的是0~100需要缩小
|
||||
OpenAIPromptExecutionSettings settings = new() { Temperature = temperature };
|
||||
if (!string.IsNullOrEmpty(app.ApiFunctionList))
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
_kernelService.ImportFunctionsByApp(app, _kernel);
|
||||
settings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, Temperature = temperature };
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
|
||||
|
||||
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
result = answers;
|
||||
}
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
|
||||
}
|
||||
return result;
|
||||
//var promptTemplateFactory = new KernelPromptTemplateFactory();
|
||||
//var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
|
||||
//var renderedPrompt = await promptTemplate.RenderAsync(_kernel);
|
||||
//Console.WriteLine(renderedPrompt);
|
||||
|
||||
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
|
||||
var chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
int i = 0;
|
||||
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
result.choices[0].delta.content = content.Content.ConvertToString();
|
||||
string message = $"data: {JsonConvert.SerializeObject(result)}\n\n";
|
||||
await HttpContext.Response.WriteAsync(message, Encoding.UTF8);
|
||||
await HttpContext.Response.Body.FlushAsync();
|
||||
//模拟延迟。
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
await HttpContext.Response.WriteAsync("data: [DONE]");
|
||||
await HttpContext.Response.Body.FlushAsync();
|
||||
|
||||
await HttpContext.Response.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -99,16 +140,23 @@ namespace AntSK.Services.OpenApi
|
||||
private async Task<string> SendChat( string msg, Apps app)
|
||||
{
|
||||
string result = "";
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
if (string.IsNullOrEmpty(app.Prompt) || !app.Prompt.Contains("{{$input}}"))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$input}}";
|
||||
app.Prompt = app.Prompt.ConvertToString() + "{{$input}}";
|
||||
}
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
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 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 func = _kernel.CreateFunctionFromPrompt(app.Prompt, settings);
|
||||
var chatResult = await _kernel.InvokeAsync(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
if (chatResult.IsNotNull())
|
||||
{
|
||||
@@ -118,6 +166,48 @@ namespace AntSK.Services.OpenApi
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> SendKms(string msg, Apps app)
|
||||
{
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
string result = "";
|
||||
//知识库问答
|
||||
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(msg, 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 = await _kernel.InvokeAsync(function: jsonFun,
|
||||
arguments: new KernelArguments() { ["data"] = dataMsg, ["history"] = "", ["questions"] = msg });
|
||||
if (chatResult.IsNotNull())
|
||||
{
|
||||
string answers = chatResult.GetValue<string>();
|
||||
result = answers;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
@@ -126,7 +216,7 @@ namespace AntSK.Services.OpenApi
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(OpenAIModel model)
|
||||
{
|
||||
|
||||
var _kernel = _kernelService.GetKernel();
|
||||
StringBuilder history = new StringBuilder();
|
||||
string questions = model.messages[model.messages.Count-1].content;
|
||||
for(int i=0;i<model.messages.Count()-1;i++)
|
||||
@@ -135,11 +225,17 @@ namespace AntSK.Services.OpenApi
|
||||
history.Append($"{item.role}:{item.content}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
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 = $"历史对话:{his}{Environment.NewLine}用户问题:{Environment.NewLine}{questions}"; ;
|
||||
return msg;
|
||||
if (model.messages.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,15 +27,19 @@
|
||||
"Postgres": "Host=;Port=;Database=antsk;Username=;Password="
|
||||
},
|
||||
"OpenAIOption": {
|
||||
"EndPoint": "",
|
||||
"Key": "",
|
||||
"Model": "",
|
||||
"EmbeddingModel": ""
|
||||
"EndPoint": "https://localhost:5001/llama/",
|
||||
"Key": "不能为空",
|
||||
"Model": "gpt-3.5-turbo",
|
||||
"EmbeddingModel": "text-embedding-ada-002"
|
||||
},
|
||||
"Postgres": {
|
||||
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
|
||||
"TableNamePrefix": "km-"
|
||||
},
|
||||
"LLamaSharp": {
|
||||
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf",
|
||||
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf"
|
||||
},
|
||||
"Login": {
|
||||
"User": "admin",
|
||||
"Password": "xuzeyu"
|
||||
|
||||
30
AntSK/plugins/KMSPlugin/Ask/config.json
Normal file
30
AntSK/plugins/KMSPlugin/Ask/config.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"type": "completion",
|
||||
"description": "知识库问答",
|
||||
"completion": {
|
||||
"temperature": 0.0,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 0.0
|
||||
},
|
||||
"input": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "doc",
|
||||
"description": "背景文档。",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"name": "history",
|
||||
"description": "历史聊天记录。",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"name": "questions",
|
||||
"description": "用户问题。",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
11
AntSK/plugins/KMSPlugin/Ask/skprompt.txt
Normal file
11
AntSK/plugins/KMSPlugin/Ask/skprompt.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Facts:
|
||||
====
|
||||
{{$doc}}
|
||||
|
||||
Given only the facts above, provide a comprehensive/detailed answer.
|
||||
You don't know where the knowledge comes from, just answer.
|
||||
Answer in the same language as the question,
|
||||
If you don't have sufficient information, reply with '知识库未搜索到相关内容'.
|
||||
History:{{$history}}
|
||||
Question: {{$questions}}
|
||||
Answer:
|
||||
BIN
AntSK/wwwroot/assets/gzh.png
Normal file
BIN
AntSK/wwwroot/assets/gzh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
@@ -17,10 +17,40 @@
|
||||
"key": "kms",
|
||||
"icon": "database"
|
||||
},
|
||||
{
|
||||
"path": "/plugins",
|
||||
"name": "插件市场",
|
||||
"key": "plugins",
|
||||
"icon": "dropbox",
|
||||
"children": [
|
||||
{
|
||||
"path": "/plugins/apilist",
|
||||
"name": "Api管理",
|
||||
"key": "plugins.apilist"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/setting",
|
||||
"name": "设置",
|
||||
"key": "setting",
|
||||
"icon": "setting"
|
||||
"icon": "setting",
|
||||
"children": [
|
||||
{
|
||||
"path": "/setting/userlist",
|
||||
"name": "用户管理",
|
||||
"key": "setting.user"
|
||||
},
|
||||
{
|
||||
"path": "/setting/modellist",
|
||||
"name": "模型管理",
|
||||
"key": "setting.modellist"
|
||||
},
|
||||
{
|
||||
"path": "/setting/modeldown",
|
||||
"name": "模型下载",
|
||||
"key": "setting.modeldown"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
26
Dockerfile
26
Dockerfile
@@ -1,13 +1,25 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
# Build stage
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy csproj and restore as distinct layers
|
||||
COPY ["AntSK/AntSK.csproj", "AntSK/"]
|
||||
RUN dotnet restore "AntSK/AntSK.csproj"
|
||||
|
||||
# Copy everything else and build
|
||||
COPY . .
|
||||
WORKDIR "/src/AntSK"
|
||||
RUN dotnet build "AntSK.csproj" -c Release -o /app/build
|
||||
RUN dotnet publish "AntSK.csproj" -c Release -o /app/publish
|
||||
|
||||
# Runtime stage
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
WORKDIR /service
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /app
|
||||
COPY ["AntSK/bin/Release/net8.0/publish", "publish"]
|
||||
|
||||
WORKDIR /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/publish .
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo 'Asia/Shanghai' >/etc/timezone
|
||||
ENTRYPOINT ["dotnet", "AntSK.dll"]
|
||||
ENTRYPOINT ["dotnet", "AntSK.dll"]
|
||||
|
||||
18
README.en.md
18
README.en.md
@@ -35,6 +35,8 @@
|
||||
|
||||
- **API interface publishing**: internal functions are provided externally in the form of API, so that developers can easily translate Xzy AntSK KnowledgeBase is integrated into other applications to enhance application intelligence.
|
||||
|
||||
- **Model management**: Adapt and manage different models from different vendors.
|
||||
|
||||
|
||||
|
||||
## Application scenarios
|
||||
@@ -157,7 +159,23 @@ Postgres:{
|
||||
|
||||
I use CodeFirst mode. As long as the database link is configured, the table structure is automatically created
|
||||
|
||||
If you want to use LLamaSharp to run the local model, you also need to set the following configuration:
|
||||
```
|
||||
"LLamaSharp": {
|
||||
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf",
|
||||
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf"
|
||||
},
|
||||
```
|
||||
|
||||
You need to configure the addresses of the Chat and Embedding models, and then modify EndPoint to local. When using the local model, parameters such as Key, Model, and Embedding Model are not used, so you can freely fill in these parameters:
|
||||
```
|
||||
"OpenAIOption": {
|
||||
"EndPoint": "https://ip:port/llama/",
|
||||
"Key": "",
|
||||
"Model": "",
|
||||
"EmbeddingModel": ""
|
||||
},
|
||||
```
|
||||
|
||||
|
||||
To learn more or start using**AntSK**, you can follow my public account and join the exchange group.
|
||||
|
||||
48
README.md
48
README.md
@@ -18,6 +18,8 @@
|
||||
|
||||
- **API接口发布**:将内部功能以API的形式对外提供,便于开发者将AntSK 集成进其他应用,增强应用智慧。
|
||||
|
||||
- **模型管理**:适配和管理集成不同厂商的不同模型。
|
||||
|
||||
## 应用场景
|
||||
|
||||
AntSK 适用于多种业务场景,例如:
|
||||
@@ -60,18 +62,36 @@ AntSK 适用于多种业务场景,例如:
|
||||
## 如何开始?
|
||||
|
||||
在这里我使用的是Postgres 作为数据存储和向量存储,因为Semantic Kernel和Kernel Memory都支持他,当然你也可以换成其他的。
|
||||
|
||||
模型默认支持openai,如果需要使用azure openai需要调整SK的依赖注入,也可以使用one-api进行集成。
|
||||
|
||||
Login是默认的登陆账号和密码
|
||||
|
||||
需要配置如下的配置文件
|
||||
|
||||
## 使用docker-compose
|
||||
|
||||
从项目根目录下载docker-compose.yml,然后把配置文件appsettings.json和它放在统一目录,
|
||||
|
||||
这里已经把pg的镜像做好了。在docker-compose.yml中可以修改默认账号密码,然后你的appsettings.json的数据库连接需要保持一致。
|
||||
|
||||
然后你可以进入到目录后执行
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
来启动AntSK
|
||||
|
||||
|
||||
## 配置文件的一些含义
|
||||
```
|
||||
"ConnectionStrings": {
|
||||
"Postgres": "Host=;Port=;Database=antsk;Username=;Password="
|
||||
"Postgres": "Host=;Port=;Database=antsk;Username=;Password="//这个是业务数据的连接字符串
|
||||
},
|
||||
"OpenAIOption": {
|
||||
"EndPoint": "",
|
||||
"Key": "",
|
||||
"Model": "",
|
||||
"EmbeddingModel": ""
|
||||
"EndPoint": "", //openai协议的接口,写到v1之前
|
||||
"Key": "",//接口秘钥,如果使用本地模型可以随意填写一个但不能为空
|
||||
"Model": "",//会话模型,使用接口时需要,使用本地模型可以随意填写
|
||||
"EmbeddingModel": ""//向量模型,使用接口时需要,使用本地模型可以随意填写
|
||||
},
|
||||
"Postgres": {
|
||||
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
|
||||
@@ -84,6 +104,24 @@ Login是默认的登陆账号和密码
|
||||
```
|
||||
我使用的是CodeFirst模式,只要配置好数据库链接,表结构是自动创建的
|
||||
|
||||
如果想使用LLamaSharp运行本地模型还需要设置如下配置:
|
||||
```
|
||||
"LLamaSharp": {
|
||||
"Chat": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf",//本地会话模型的磁盘路径
|
||||
"Embedding": "D:\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf"//本地向量模型的磁盘路径
|
||||
},
|
||||
```
|
||||
|
||||
需要配置Chat和Embedding模型的地址,然后修改EndPoint为本地,使用本地模型时并没有用到Key、Model、EmbeddingModel这些参数,所以这几个你可以随意填写:
|
||||
```
|
||||
"OpenAIOption": {
|
||||
"EndPoint": "http://ip:port/llama/",//如果使用本地模型这个ip端口是AntSK服务启动的ip和端口
|
||||
"Key": "",//接口秘钥,如果使用本地模型可以随意填写一个但不能为空
|
||||
"Model": "",//会话模型,使用接口时需要,使用本地模型可以随意填写
|
||||
"EmbeddingModel": ""//向量模型,使用接口时需要,使用本地模型可以随意填写
|
||||
},
|
||||
```
|
||||
|
||||
|
||||
想了解更多信息或开始使用 **AntSK**,可以关注我的公众号以及加入交流群。
|
||||
|
||||
|
||||
34
docker-compose.yml
Normal file
34
docker-compose.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
# 非 host 版本, 不使用本机代理
|
||||
version: '3.8'
|
||||
services:
|
||||
antskpg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/pg:v0.5.0
|
||||
container_name: antskpg
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
- 5432:5432
|
||||
networks:
|
||||
- antsk
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=antsk
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
antsk:
|
||||
container_name: antsk
|
||||
image: registry.cn-hangzhou.aliyuncs.com/xuzeyu91/antsk:v0.1.3
|
||||
ports:
|
||||
- 5000:5000
|
||||
networks:
|
||||
- antsk
|
||||
depends_on:
|
||||
- antskpg
|
||||
restart: always
|
||||
environment:
|
||||
- ASPNETCORE_URLS=http://*:5000
|
||||
volumes:
|
||||
- ./appsettings.json:/app/appsettings.json # 本地配置文件 需要放在同级目录
|
||||
networks:
|
||||
antsk:
|
||||
Reference in New Issue
Block a user