mirror of
https://github.com/AIDotNet/AntSK.git
synced 2026-02-22 09:39:19 +08:00
Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b25e429687 | ||
|
|
a3f87bf123 | ||
|
|
2cab253c4a | ||
|
|
ed119f02e2 | ||
|
|
f178fc16e9 | ||
|
|
e4b2071689 | ||
|
|
146df7ed1d | ||
|
|
7e8db0a3f8 | ||
|
|
e098922219 | ||
|
|
16ca024c22 | ||
|
|
a886e9dfb3 | ||
|
|
5d6114c9ec | ||
|
|
e3b2e1f434 | ||
|
|
6145347d9b | ||
|
|
49e694cbdc | ||
|
|
f860229993 | ||
|
|
f5d93baa17 | ||
|
|
76f58c43b0 | ||
|
|
db29fcc867 | ||
|
|
2f361c23c7 | ||
|
|
4e7ac6eb4b | ||
|
|
58bca689c8 | ||
|
|
e1b2aee33c | ||
|
|
7a6e8525c5 | ||
|
|
b8db68bed1 | ||
|
|
1072f47467 | ||
|
|
cfb1d858c6 | ||
|
|
8e0e75aee6 | ||
|
|
1479c942e2 | ||
|
|
63b02b889c | ||
|
|
5a1de0e2cf | ||
|
|
ad2a402521 | ||
|
|
a14af93afc | ||
|
|
90630bca1d | ||
|
|
bf1db38e2e | ||
|
|
88e23768c6 | ||
|
|
ea8366ca92 | ||
|
|
8e1f07415f | ||
|
|
0b62e773eb | ||
|
|
1c4b1e2d2f | ||
|
|
0e53dd3854 | ||
|
|
c2fa13b6ab | ||
|
|
837c9a1444 | ||
|
|
08ec4f6b7e | ||
|
|
f5a8076da3 | ||
|
|
cf8c935694 | ||
|
|
67fd7d952a | ||
|
|
b7a3ad6fe7 | ||
|
|
0dabd2ee58 | ||
|
|
e841fc6282 | ||
|
|
ab45a46fc0 | ||
|
|
34c06b1dd1 | ||
|
|
50415b12c0 | ||
|
|
61509154c6 | ||
|
|
3d5be56089 | ||
|
|
a0da9c223b | ||
|
|
172755081f | ||
|
|
1da43f2cee | ||
|
|
8b77ea4438 | ||
|
|
b6039c0da3 | ||
|
|
1cda01d5b3 | ||
|
|
6bd2ef4131 | ||
|
|
96e1ca6b34 | ||
|
|
6620a74447 | ||
|
|
fb7a85b9cf | ||
|
|
b7532676c5 | ||
|
|
8cd6940311 | ||
|
|
2de851f5b1 | ||
|
|
e8fd055349 | ||
|
|
7b92373f54 | ||
|
|
03b937432f | ||
|
|
bd9798ad17 | ||
|
|
030abf1059 | ||
|
|
91e633700a | ||
|
|
6eb034967c |
@@ -8,15 +8,22 @@
|
||||
</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="Microsoft.SemanticKernel" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.Core" Version="0.26.240121.1" />
|
||||
<PackageReference Include="Microsoft.KernelMemory.MemoryDb.Postgres" Version="0.26.240121.1" />
|
||||
<PackageReference Include="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="LLamaSharp" Version="0.10.0" />
|
||||
<PackageReference Include="LLamaSharp.Backend.Cpu" Version="0.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -57,6 +57,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开始
|
||||
@@ -102,6 +107,11 @@
|
||||
知识库ID列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.Apps.SecretKey">
|
||||
<summary>
|
||||
API调用秘钥
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:AntSK.Domain.Repositories.KmsDetails.FileName">
|
||||
<summary>
|
||||
文件名称
|
||||
@@ -399,6 +409,31 @@
|
||||
sqlserver连接
|
||||
</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
|
||||
|
||||
@@ -19,23 +19,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,13 @@ namespace AntSK.Domain.Model
|
||||
public class MessageInfo
|
||||
{
|
||||
public string ID { get; set; } = "";
|
||||
public string Questions { get; set; } = "";
|
||||
public string Answers { get; set; } = "";
|
||||
public string Context { get; set; } = "";
|
||||
public string HtmlAnswers { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 发送是true 接收是false
|
||||
/// </summary>
|
||||
public bool IsSend { get; set; } = false;
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
}
|
||||
|
||||
15
AntSK.Domain/Options/LLamaSharpOption.cs
Normal file
15
AntSK.Domain/Options/LLamaSharpOption.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Options
|
||||
{
|
||||
public class LLamaSharpOption
|
||||
{
|
||||
public static string Chat { get; set; }
|
||||
|
||||
public static string Embedding { get; set; }
|
||||
}
|
||||
}
|
||||
15
AntSK.Domain/Options/LoginOption.cs
Normal file
15
AntSK.Domain/Options/LoginOption.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Domain.Options
|
||||
{
|
||||
public class LoginOption
|
||||
{
|
||||
public static string User { get; set; }
|
||||
|
||||
public static string Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -39,5 +39,9 @@ namespace AntSK.Domain.Repositories
|
||||
/// 知识库ID列表
|
||||
/// </summary>
|
||||
public string? KmsIdList { get; set; }
|
||||
/// <summary>
|
||||
/// API调用秘钥
|
||||
/// </summary>
|
||||
public string? SecretKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,20 @@ 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 (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 +34,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 +54,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,14 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DocumentationFile>AntSK.xml</DocumentationFile>
|
||||
<NoWarn>CA1050,CA1707,CA2007,VSTHRD111,CS1591,RCS1110,CA5394,SKEXP0001,SKEXP0002,SKEXP0003,SKEXP0004,SKEXP0010,SKEXP0011,,SKEXP0012,SKEXP0020,SKEXP0021,SKEXP0022,SKEXP0023,SKEXP0024,SKEXP0025,SKEXP0026,SKEXP0027,SKEXP0028,SKEXP0029,SKEXP0030,SKEXP0031,SKEXP0032,SKEXP0040,SKEXP0041,SKEXP0042,SKEXP0050,SKEXP0051,SKEXP0052,SKEXP0053,SKEXP0054,SKEXP0055,SKEXP0060,SKEXP0061,SKEXP0101,SKEXP0102</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AntDesign.Charts" Version="0.5.1" />
|
||||
<PackageReference Include="AntDesign.ProLayout" Version="0.17.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
@@ -17,9 +20,5 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AntSK.Domain\AntSK.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\Setting\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
115
AntSK/AntSK.xml
115
AntSK/AntSK.xml
@@ -16,6 +16,85 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.LLamaSharpController.chat(AntSK.Models.OpenAIModel)">
|
||||
<summary>
|
||||
本地会话接口
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.LLamaSharpController.embedding(AntSK.Models.OpenAIEmbeddingModel)">
|
||||
<summary>
|
||||
本地嵌入接口
|
||||
</summary>
|
||||
<param name="model"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Controllers.OpenController">
|
||||
<summary>
|
||||
对外接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.OpenController.#ctor(AntSK.Services.OpenApi.IOpenApiService)">
|
||||
<summary>
|
||||
对外接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Controllers.OpenController.chat(AntSK.Models.OpenAIModel)">
|
||||
<summary>
|
||||
对话接口
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.Chat.SendKms(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.Chat.SendChat(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送普通对话
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.Chat.HistorySummarize(System.String)">
|
||||
<summary>
|
||||
历史会话的会话总结
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.OpenChat.SendKms(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.OpenChat.SendChat(System.String,System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送普通对话
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Pages.ChatPage.OpenChat.HistorySummarize(System.String)">
|
||||
<summary>
|
||||
历史会话的会话总结
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Pages.KmsPage.KmsDetail.UrlModel">
|
||||
<summary>
|
||||
根据文档ID获取文档
|
||||
@@ -23,5 +102,41 @@
|
||||
<param name="fileid"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:AntSK.Services.LLamaSharp.LLamaChatService">
|
||||
<summary>
|
||||
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:AntSK.Services.LLamaSharp.LLamaEmbeddingService">
|
||||
<summary>
|
||||
本地Embedding
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.SendKms(System.String,AntSK.Domain.Repositories.Apps)">
|
||||
<summary>
|
||||
发送知识库问答
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<param name="app"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:AntSK.Services.OpenApi.OpenApiService.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.HistorySummarize(AntSK.Models.OpenAIModel)">
|
||||
<summary>
|
||||
历史会话的会话总结
|
||||
</summary>
|
||||
<param name="questions"></param>
|
||||
<param name="msg"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<CascadingValue Value="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
|
||||
</CascadingValue>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(BasicLayout)">
|
||||
<p>Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<AntContainer />
|
||||
@inject NavigationManager NavigationManager
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<CascadingValue Value="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(BasicLayout)" />
|
||||
</CascadingValue>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(BasicLayout)">
|
||||
<AntSK.Pages.Exception._404/>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<AntContainer />
|
||||
</CascadingAuthenticationState>
|
||||
|
||||
@code {
|
||||
private RenderFragment RedirectToLogin => builder =>
|
||||
{
|
||||
var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
|
||||
if (string.IsNullOrWhiteSpace(returnUrl))
|
||||
{
|
||||
NavigationManager.NavigateTo("user/login");
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo($"user/login?returnUrl={Uri.EscapeDataString(returnUrl)}", true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
@namespace AntSK.Components
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@inherits AntDomComponentBase
|
||||
|
||||
<Space Class="@ClassMapper.Class" Size="@("24")">
|
||||
<Space Class="@ClassMapper.Class" Size="@("26")">
|
||||
<SpaceItem>
|
||||
<AvatarDropdown Name="@_currentUser.Name"
|
||||
<AvatarDropdown Name="@context.Identity.Name"
|
||||
Avatar="@_currentUser.Avatar"
|
||||
MenuItems="@AvatarMenuItems"
|
||||
OnItemSelected="HandleSelectUser" />
|
||||
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@@ -6,6 +6,10 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Models;
|
||||
using AntSK.Services;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Security.Claims;
|
||||
using AntSK.Services.Auth;
|
||||
using AntSK.Domain.Options;
|
||||
|
||||
namespace AntSK.Components
|
||||
{
|
||||
@@ -38,7 +42,6 @@ namespace AntSK.Components
|
||||
|
||||
public AvatarMenuItem[] AvatarMenuItems { get; set; } = new AvatarMenuItem[]
|
||||
{
|
||||
new() { Key = "center", IconType = "user", Option = "个人中心"},
|
||||
new() { Key = "setting", IconType = "setting", Option = "个人设置"},
|
||||
new() { IsDivider = true },
|
||||
new() { Key = "logout", IconType = "logout", Option = "退出登录"}
|
||||
@@ -50,6 +53,11 @@ namespace AntSK.Components
|
||||
[Inject] protected IProjectService ProjectService { get; set; }
|
||||
[Inject] protected MessageService MessageService { get; set; }
|
||||
|
||||
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
|
||||
[Inject] protected MessageService? Message { get; set; }
|
||||
|
||||
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
@@ -73,11 +81,15 @@ namespace AntSK.Components
|
||||
{
|
||||
switch (item.Key)
|
||||
{
|
||||
case "center":
|
||||
NavigationManager.NavigateTo("/account/center");
|
||||
break;
|
||||
case "setting":
|
||||
NavigationManager.NavigateTo("/account/settings");
|
||||
if (context.Identity.Name != LoginOption.User)
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/user/info/" + context.Identity.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Message.Info("管理员无需设置", 2);
|
||||
}
|
||||
break;
|
||||
case "logout":
|
||||
NavigationManager.NavigateTo("/user/login");
|
||||
|
||||
44
AntSK/Controllers/LLamaSharpController.cs
Normal file
44
AntSK/Controllers/LLamaSharpController.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
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)
|
||||
{
|
||||
if (model.stream)
|
||||
{
|
||||
await _lLamaSharpService.ChatStream(model, HttpContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _lLamaSharpService.Chat(model, HttpContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地嵌入接口
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("llama/v1/embeddings")]
|
||||
public async Task embedding(OpenAIEmbeddingModel model)
|
||||
{
|
||||
|
||||
await _lLamaSharpService.Embedding(model,HttpContext);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
31
AntSK/Controllers/OpenController.cs
Normal file
31
AntSK/Controllers/OpenController.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Models;
|
||||
using AntSK.Models.OpenAPI;
|
||||
using AntSK.Services.OpenApi;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AntSK.Controllers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 对外接口
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
public class OpenController(IOpenApiService _openApiService) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 对话接口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Route("api/v1/chat/completions")]
|
||||
public async Task chat(OpenAIModel model)
|
||||
{
|
||||
string sk = HttpContext.Request.Headers["Authorization"].ConvertToString();
|
||||
await _openApiService.Chat(model,sk, HttpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
@namespace AntSK
|
||||
@using System.Security.Claims
|
||||
@using AntSK.Services.Auth
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using AntSK.Domain.Options
|
||||
@using AntSK.Domain.Repositories
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<AntDesign.ProLayout.BasicLayout
|
||||
@@ -21,11 +26,28 @@
|
||||
private MenuDataItem[] _menuData = { };
|
||||
|
||||
[Inject] public HttpClient HttpClient { get; set; }
|
||||
[Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
|
||||
[Inject] protected IUsers_Repositories _users_Repositories { get; set; }
|
||||
private ClaimsPrincipal context => ((AntSKAuthProvider)AuthenticationStateProvider).GetCurrentUser();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_menuData = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
|
||||
//菜单权限控制
|
||||
var menuList = await HttpClient.GetFromJsonAsync<MenuDataItem[]>("data/menu.json");
|
||||
if ((bool)context?.Identity.IsAuthenticated)
|
||||
{
|
||||
if (context.Identity.Name == LoginOption.User)
|
||||
{
|
||||
_menuData = menuList;
|
||||
}
|
||||
else
|
||||
{
|
||||
var userMenuList = _users_Repositories.GetFirst(p => p.No == context.Identity.Name).MenuRole.Split(",").ToList();
|
||||
//非管理员用户不允许操作系统设置
|
||||
_menuData = menuList.Where(p => p.Key != "setting" && userMenuList.Contains(p.Key)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
8
AntSK/Layouts/OpenLayout.razor
Normal file
8
AntSK/Layouts/OpenLayout.razor
Normal file
@@ -0,0 +1,8 @@
|
||||
@namespace AntSK
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@Body
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -11,14 +11,14 @@
|
||||
<div class="header">
|
||||
<a>
|
||||
<img alt="logo" class="logo" src="assets/logo.svg" />
|
||||
<span class="title">Ant Design</span>
|
||||
<span class="title">AntSK</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="desc">Ant Design Blazor</div>
|
||||
<div class="desc"> </div>
|
||||
</div>
|
||||
@Body
|
||||
</div>
|
||||
<FooterView Copyright="2021 Ant Design Blazor" Links="Links"></FooterView>
|
||||
<FooterView Copyright="许泽宇的技术分享" Links="Links"></FooterView>
|
||||
</div>
|
||||
|
||||
@code
|
||||
|
||||
28
AntSK/Models/OpenAPI/OpenAIModel.cs
Normal file
28
AntSK/Models/OpenAPI/OpenAIModel.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AntSK.Models
|
||||
{
|
||||
public class OpenAIModel
|
||||
{
|
||||
public bool stream { get; set; } = false;
|
||||
public List<OpenAIMessage> messages { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIMessage
|
||||
{
|
||||
public string role { get; set; }
|
||||
|
||||
public string content { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIEmbeddingModel
|
||||
{
|
||||
public List<string> input { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
67
AntSK/Models/OpenAPI/OpenAIResult.cs
Normal file
67
AntSK/Models/OpenAPI/OpenAIResult.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AntSK.Models.OpenAPI
|
||||
{
|
||||
public class OpenAIResult
|
||||
{
|
||||
public string id { get; set; } = Guid.NewGuid().ToString();
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "chat.completion";
|
||||
public List<ChoicesModel> choices { get; set; }
|
||||
public long created { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class ChoicesModel
|
||||
{
|
||||
public string finish_reason { get; set; } = "stop";
|
||||
public int index { get; set; } = 0;
|
||||
|
||||
public OpenAIMessage message { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAIEmbeddingResult
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "list";
|
||||
public string model { get; set; } = "ada";
|
||||
|
||||
public UsageModel usage { get; set; } = new UsageModel();
|
||||
|
||||
public List<DataModel> data { get; set; } = new List<DataModel>() { new DataModel() };
|
||||
}
|
||||
|
||||
public class UsageModel
|
||||
{
|
||||
public long prompt_tokens { get; set; } = 0;
|
||||
|
||||
public long total_tokens { get; set; } = 0;
|
||||
}
|
||||
|
||||
public class DataModel
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "embedding";
|
||||
public int index { get; set; } = 0;
|
||||
|
||||
public List<float> embedding { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class OpenAIStreamResult
|
||||
{
|
||||
public string id { get; set; } = Guid.NewGuid().ToString();
|
||||
[JsonProperty("object")]
|
||||
public string obj { get; set; } = "chat.completion";
|
||||
public List<StreamChoicesModel> choices { get; set; }
|
||||
public long created { get; set; }
|
||||
}
|
||||
public class StreamChoicesModel
|
||||
{
|
||||
public int index { get; set; } = 0;
|
||||
|
||||
public OpenAIMessage delta { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
32
AntSK/Models/Template/LayoutModel.cs
Normal file
32
AntSK/Models/Template/LayoutModel.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
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 },
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using AntSK.Utils;
|
||||
using AntSK.Domain.Utils;
|
||||
|
||||
namespace AntSK.Models
|
||||
{
|
||||
@@ -3,6 +3,8 @@
|
||||
@using AntSK.Models
|
||||
@page "/App/Add"
|
||||
@page "/App/Add/{AppId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="新增应用">
|
||||
<ChildContent>
|
||||
@@ -10,31 +12,31 @@
|
||||
<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>
|
||||
</RadioGroup>
|
||||
</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>
|
||||
@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>
|
||||
}
|
||||
@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="选择知识库"
|
||||
@@ -51,7 +53,7 @@
|
||||
</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>
|
||||
@@ -59,13 +61,6 @@
|
||||
返回
|
||||
</Button>
|
||||
</FormItem>
|
||||
@if (!string.IsNullOrEmpty(_errorMsg))
|
||||
{
|
||||
<Alert Type="@AlertType.Error"
|
||||
Message="错误"
|
||||
Description="@_errorMsg"
|
||||
ShowIcon="true" />
|
||||
}
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace AntSK.Pages.AppPage
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
|
||||
private Apps _appModel = new Apps() ;
|
||||
|
||||
@@ -26,31 +28,6 @@ namespace AntSK.Pages.AppPage
|
||||
private List<Kmss> _kmsList = new List<Kmss>();
|
||||
|
||||
|
||||
private string _errorMsg { get; set; }
|
||||
|
||||
private readonly FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
private 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();
|
||||
@@ -68,9 +45,11 @@ namespace AntSK.Pages.AppPage
|
||||
{
|
||||
//新增
|
||||
_appModel.Id = Guid.NewGuid().ToString();
|
||||
//秘钥
|
||||
_appModel.SecretKey="sk-"+ Guid.NewGuid().ToString();
|
||||
if (_apps_Repositories.IsAny(p => p.Name == _appModel.Name))
|
||||
{
|
||||
_errorMsg = "名称已存在!";
|
||||
_ = Message.Error("名称已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@page "/App/Detail/{AppID}"
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<h3>AppDetail</h3>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntSK.Pages.AppPage
|
||||
{
|
||||
public partial class AppDetail
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
@using AntSK.Domain.Repositories
|
||||
@page "/AppList"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="应用列表">
|
||||
<Content>
|
||||
@@ -30,7 +32,9 @@
|
||||
{
|
||||
<Card Hoverable Bordered Class="card" Actions="@(new[] {
|
||||
info(()=> Info(context.Id)) ,
|
||||
open(()=> Open(context.Id)),
|
||||
delete(async ()=>await Delete(context.Id)) ,
|
||||
|
||||
})" Style="max-height:247px;">
|
||||
<CardMeta>
|
||||
<AvatarTemplate>
|
||||
@@ -44,6 +48,15 @@
|
||||
<!--todo: Ellipsis not working-->
|
||||
@context.Describe
|
||||
</Paragraph>
|
||||
@if (context.Type == "chat")
|
||||
{
|
||||
<Tag Color="@PresetColor.Yellow.ToString()">会话应用</Tag>
|
||||
}
|
||||
else if (context.Type == "kms")
|
||||
{
|
||||
<Tag Color="@PresetColor.Green.ToString()">知识库</Tag>
|
||||
|
||||
}
|
||||
</DescriptionTemplate>
|
||||
</CardMeta>
|
||||
</Card>
|
||||
@@ -57,5 +70,6 @@
|
||||
@code
|
||||
{
|
||||
RenderFragment info(Action clickAction) =>@<a key="info" @onclick="@clickAction">查看</a>;
|
||||
RenderFragment open(Action clickAction) =>@<a key="info" @onclick="@clickAction">外部使用</a>;
|
||||
RenderFragment delete(Action clickAction) => @<a key="delete" @onclick="@clickAction">删除</a> ;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,10 @@ namespace AntSK.Pages
|
||||
NavigationManager.NavigateTo($"/app/add/{id}");
|
||||
}
|
||||
|
||||
private void Open(string id)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/app/open/{id}");
|
||||
}
|
||||
|
||||
|
||||
private async Task Delete(string id)
|
||||
|
||||
60
AntSK/Pages/AppPage/AppOpen.razor
Normal file
60
AntSK/Pages/AppPage/AppOpen.razor
Normal file
@@ -0,0 +1,60 @@
|
||||
@namespace AntSK.Pages.AppPage
|
||||
@page "/App/Open/{AppID}"
|
||||
|
||||
<AntDesign.Col Lg="24" Md="24">
|
||||
<Card Class="tabsCard">
|
||||
<CardTabs>
|
||||
<Tabs DefaultActiveKey="1">
|
||||
<TabPane Key="1">
|
||||
<TabTemplate>Api接入</TabTemplate>
|
||||
<ChildContent>
|
||||
<Card>
|
||||
<Form Model="@_appModel"
|
||||
Style="margin-top: 8px;">
|
||||
<FormItem Label="接口地址" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input @bind-Value="@_openApiUrl" ReadOnly="true" />
|
||||
|
||||
</FormItem>
|
||||
<FormItem Label="秘钥" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input @bind-Value="@context.SecretKey" ReadOnly="true" />
|
||||
<Button OnClick="Reset" Style="margin-top:5px;">
|
||||
重置
|
||||
</Button>
|
||||
</FormItem>
|
||||
<FormItem Label="使用方法" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<TextArea @bind-Value="@_desc" ReadOnly="true" Rows="15" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
</TabPane>
|
||||
<TabPane Key="2">
|
||||
<TabTemplate>页面嵌入</TabTemplate>
|
||||
<ChildContent>
|
||||
<Form Model="@_appModel"
|
||||
Style="margin-top: 8px;">
|
||||
<FormItem Label="页面地址" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<Input @bind-Value="@_openChatUrl" ReadOnly="true" />
|
||||
<a href="@_openChatUrl" target="_blank">打开</a>
|
||||
</FormItem>
|
||||
<FormItem Label="嵌入脚本" LabelCol="_formItemLayout.LabelCol" WrapperCol="_formItemLayout.WrapperCol">
|
||||
<TextArea @bind-Value="@_script" ReadOnly="true" Rows="5" />
|
||||
<span>
|
||||
将上述代码添加到站点的 body 中<br/>
|
||||
data-width:宽度,默认 30rem<br />
|
||||
data-height:高度,默认 50rem<br />
|
||||
data-message-icon-url:嵌入消息图标地址,默认不设置<br />
|
||||
data-color:消息图标颜色,默认 #4e83fd<br />
|
||||
</span>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</ChildContent>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</CardTabs>
|
||||
</Card>
|
||||
</AntDesign.Col>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
90
AntSK/Pages/AppPage/AppOpen.razor.cs
Normal file
90
AntSK/Pages/AppPage/AppOpen.razor.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AntSK.Pages.AppPage
|
||||
{
|
||||
public partial class AppOpen
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected IApps_Repositories _apps_Repositories { get; set; }
|
||||
[Inject]
|
||||
IConfirmService _confirmService { get; set; }
|
||||
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
|
||||
private readonly FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
|
||||
private Apps _appModel = new Apps();
|
||||
|
||||
private string _openApiUrl { get; set; }
|
||||
|
||||
private string _openChatUrl { get; set; }
|
||||
|
||||
private string _desc { get; set; }
|
||||
|
||||
private string _script { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_appModel = _apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
_openApiUrl = NavigationManager.BaseUri + "api/v1/chat/completions";
|
||||
_openChatUrl= NavigationManager.BaseUri + "openchat/"+AppId;
|
||||
GetDesc();
|
||||
GetScript();
|
||||
}
|
||||
|
||||
private void GetDesc()
|
||||
{
|
||||
_desc = @$"为了方便其他应用对接,接口符合openai规范,省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List<OpenAIMessage>() { new OpenAIMessage() { role = "user", content = "你好,你是谁" } } }, Formatting.Indented)}";
|
||||
}
|
||||
|
||||
private void GetScript()
|
||||
{
|
||||
_script = $"<script src=\"{NavigationManager.BaseUri}js/iframe.js\" data-width=\"40rem\" data-height=\"80vh\" id=\"antsk-iframe\" data-src=\"{NavigationManager.BaseUri}openchat/{AppId}\" data-color=\"#4e83fd\" data-message-icon-url=\"{NavigationManager.BaseUri}assets/ai.png\"></script>";
|
||||
}
|
||||
|
||||
private void HandleSubmit()
|
||||
{
|
||||
}
|
||||
|
||||
private async Task Reset()
|
||||
{
|
||||
var content = "是否确认重置秘钥,重之后之前的秘钥将无法访问API接口";
|
||||
var title = "重置";
|
||||
var result = await _confirmService.Show(content, title, ConfirmButtons.YesNo);
|
||||
if (result == ConfirmResult.Yes)
|
||||
{
|
||||
_appModel.SecretKey = "sk-" + Guid.NewGuid().ToString();
|
||||
_apps_Repositories.Update(_appModel);
|
||||
GetDesc();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,15 @@
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@page "/Chat"
|
||||
@page "/Chat/{AppId}"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<GridRow Gutter="(16, 16)">
|
||||
<GridCol Span="12">
|
||||
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
|
||||
<Card Style="height:800px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="setting" /> 选择应用
|
||||
<GridRow Gutter="(16, 16)">
|
||||
<GridCol Span="12">
|
||||
<Spin Size="large" Tip="请稍等..." Spinning="@(_loading)">
|
||||
<Card Style="height:700px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="setting" /> 选择应用
|
||||
<Select DataSource="@_list"
|
||||
@bind-Value="@AppId"
|
||||
DefaultValue="@("lucy")"
|
||||
@@ -18,67 +20,133 @@
|
||||
LabelProperty="c=>c.Name"
|
||||
Style="width:200px">
|
||||
</Select>
|
||||
</TitleTemplate>
|
||||
<Body>
|
||||
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
|
||||
<GridRow Gutter="(8, 8)">
|
||||
<Virtualize Items="@(MessageList.OrderByDescending(o => o.CreateTime).ToList())" Context="item">
|
||||
<GridCol Span="24">
|
||||
<Card Size="small">
|
||||
<TitleTemplate>
|
||||
<Text Strong><Icon Type="bulb" /> @(item.Questions)</Text>
|
||||
</TitleTemplate>
|
||||
<Extra>
|
||||
<Space>
|
||||
<SpaceItem>
|
||||
<a style="color: gray;" @onclick="@(() => OnCopyAsync(item))"><Icon Type="copy" /></a>
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<a style="color: gray;" @onclick="@(() => OnClearAsync(item.ID))"><Icon Type="rest" /></a>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</Extra>
|
||||
<Body>
|
||||
@((MarkupString)(item.HtmlAnswers))
|
||||
</Body>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</Virtualize>
|
||||
</GridRow>
|
||||
</div>
|
||||
<div style="height: 10px;"></div>
|
||||
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())">
|
||||
<Suffix>
|
||||
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())"></Button>
|
||||
</Suffix>
|
||||
</AntDesign.Input>
|
||||
</Body>
|
||||
</Card>
|
||||
</Spin>
|
||||
</GridCol>
|
||||
<GridCol Span="12">
|
||||
<Card Style="height: 800px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="search" /> 调试结果
|
||||
</TitleTemplate>
|
||||
<Extra>
|
||||
|
||||
</Extra>
|
||||
<Body>
|
||||
<AntList Bordered DataSource="@RelevantSources">
|
||||
<ChildContent Context="item">
|
||||
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
|
||||
<ListItem>
|
||||
@item.Text
|
||||
</ListItem>
|
||||
</ChildContent>
|
||||
</AntList>
|
||||
<div id="scrollDiv" style="height: 530px; overflow-y: auto; overflow-x: hidden;">
|
||||
<GridRow Gutter="(8, 8)" Style="margin:0">
|
||||
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
|
||||
@if (item.IsSend)
|
||||
{
|
||||
<GridRow Style="width:100%">
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble sent">
|
||||
@(item.Context)
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
else
|
||||
{
|
||||
<GridRow Style="width:100%">
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" />
|
||||
</GridCol>
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble received">
|
||||
@((MarkupString)(item.HtmlAnswers))
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
</Virtualize>
|
||||
</GridRow>
|
||||
</div>
|
||||
<div style="height: 10px;"></div>
|
||||
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
|
||||
<Suffix>
|
||||
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
|
||||
</Suffix>
|
||||
</AntDesign.Input>
|
||||
</Body>
|
||||
</Card>
|
||||
</Spin>
|
||||
</GridCol>
|
||||
<GridCol Span="12">
|
||||
<Card Style="height: 700px;overflow: auto;">
|
||||
<TitleTemplate>
|
||||
<Icon Type="search" /> 调试结果
|
||||
</TitleTemplate>
|
||||
<Extra>
|
||||
|
||||
</Extra>
|
||||
<Body>
|
||||
<AntList Bordered DataSource="@RelevantSources" Style="padding:10px;">
|
||||
<ChildContent Context="item" >
|
||||
<span> <b>@item.SourceName </b> 相似度:<Text Mark> @item.Relevance</Text></span>
|
||||
<Body>
|
||||
@((MarkupString)(@item.Text))
|
||||
</Body>
|
||||
</ChildContent>
|
||||
</AntList>
|
||||
</Body>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
|
||||
<style>
|
||||
|
||||
@code {
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
width: 350px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
margin-bottom: 0;
|
||||
border-radius: 5px;
|
||||
max-width: 70%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.received {
|
||||
background-color: #f0f0f0;
|
||||
align-self: flex-start;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.sent {
|
||||
background-color: #daf8cb;
|
||||
align-self: flex-end;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: block;
|
||||
font-size: 0.75em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.received .timestamp {
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sent .timestamp {
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
@code {
|
||||
|
||||
}
|
||||
|
||||
@@ -4,15 +4,18 @@ using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Core;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
@@ -37,6 +40,7 @@ namespace AntSK.Pages.ChatPage
|
||||
protected List<MessageInfo> MessageList = [];
|
||||
protected string? _messageInput;
|
||||
protected string _json = "";
|
||||
protected bool Sendding = false;
|
||||
|
||||
List<RelevantSource> RelevantSources = new List<RelevantSource>();
|
||||
|
||||
@@ -60,15 +64,26 @@ namespace AntSK.Pages.ChatPage
|
||||
return;
|
||||
}
|
||||
|
||||
MessageList.Add(new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Context = _messageInput,
|
||||
CreateTime = DateTime.Now,
|
||||
IsSend = true
|
||||
});
|
||||
|
||||
|
||||
Sendding = true;
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
Sendding = false;
|
||||
|
||||
}
|
||||
protected async Task OnCopyAsync(MessageInfo item)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_messageInput = item.Questions;
|
||||
_messageInput = item.Context;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,82 +97,165 @@ namespace AntSK.Pages.ChatPage
|
||||
|
||||
protected async Task<bool> SendAsync(string questions)
|
||||
{
|
||||
string msg = questions;
|
||||
//处理多轮会话
|
||||
if (MessageList.Count > 0)
|
||||
{
|
||||
msg = await HistorySummarize(questions);
|
||||
}
|
||||
|
||||
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
switch (app.Type)
|
||||
{
|
||||
case "chat":
|
||||
//普通会话
|
||||
var promptTemplateFactory = new KernelPromptTemplateFactory();
|
||||
var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
|
||||
var renderedPrompt = await promptTemplate.RenderAsync(_kernel);
|
||||
|
||||
var func = _kernel.CreateFunctionFromPrompt(app.Prompt, new OpenAIPromptExecutionSettings() );
|
||||
var chatResult = await _kernel.InvokeAsync(func,new KernelArguments() { ["input"]=questions});
|
||||
if (chatResult.IsNotNull())
|
||||
{
|
||||
string answers = chatResult.GetValue<string>();
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Questions = questions,
|
||||
Answers = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info);
|
||||
}
|
||||
|
||||
await SendChat(questions, msg, app);
|
||||
break;
|
||||
case "kms":
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(questions, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Questions = questions,
|
||||
Answers = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info);
|
||||
}
|
||||
|
||||
foreach (var x in kmsResult.RelevantSources)
|
||||
{
|
||||
foreach (var xsd in x.Partitions)
|
||||
{
|
||||
string sourceName = x.SourceName;
|
||||
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == x.SourceName);
|
||||
if (fileDetail.IsNotNull())
|
||||
{
|
||||
sourceName = fileDetail.FileName;
|
||||
}
|
||||
RelevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = xsd.Text, Relevance = xsd.Relevance });
|
||||
}
|
||||
}
|
||||
}
|
||||
await SendKms(questions, msg, app);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info1 = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Context = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info1);
|
||||
}
|
||||
|
||||
foreach (var x in kmsResult.RelevantSources)
|
||||
{
|
||||
foreach (var xsd in x.Partitions)
|
||||
{
|
||||
var markdown = new Markdown();
|
||||
string sourceName = x.SourceName;
|
||||
var fileDetail = _kmsDetails_Repositories.GetFirst(p => p.FileGuidName == x.SourceName);
|
||||
if (fileDetail.IsNotNull())
|
||||
{
|
||||
sourceName = fileDetail.FileName;
|
||||
}
|
||||
RelevantSources.Add(new RelevantSource() { SourceName = sourceName, Text = markdown.Transform(xsd.Text), Relevance = xsd.Relevance });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendChat(string questions, string msg, Apps app)
|
||||
{
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$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 chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
MessageInfo info = null;
|
||||
var markdown = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.HtmlAnswers += content.Content;
|
||||
await Task.Delay(50);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
if (info.IsNotNull())
|
||||
{
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(string questions)
|
||||
{
|
||||
if (MessageList.Count > 1)
|
||||
{
|
||||
StringBuilder history = new StringBuilder();
|
||||
foreach (var item in MessageList)
|
||||
{
|
||||
if (item.IsSend)
|
||||
{
|
||||
history.Append($"user:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Append($"assistant:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return questions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RelevantSource
|
||||
|
||||
114
AntSK/Pages/ChatPage/OpenChat.razor
Normal file
114
AntSK/Pages/ChatPage/OpenChat.razor
Normal file
@@ -0,0 +1,114 @@
|
||||
@namespace AntSK.Pages.ChatPage
|
||||
@using AntSK.Domain.Repositories
|
||||
@using AntSK.Models
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@page "/OpenChat/{AppId}"
|
||||
@layout OpenLayout
|
||||
|
||||
<div id="chat" style="display:flex; flex-direction:column; height:100%; overflow-x:hidden;">
|
||||
<PageHeader Class="site-page-header" Title="@app.Name" Subtitle="@app.Describe" />
|
||||
<div id="scrollDiv" style="flex:1; width:100%; overflow-y:auto; overflow-x:hidden;padding:10px;">
|
||||
<Virtualize Items="@(MessageList.OrderBy(o => o.CreateTime).ToList())" Context="item">
|
||||
@if (item.IsSend)
|
||||
{
|
||||
<GridRow>
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble sent">
|
||||
@(item.Context)
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" />
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
else
|
||||
{
|
||||
<GridRow>
|
||||
<GridCol Span="1">
|
||||
<Image Width="100%" Style="margin-top:10px;" Src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" />
|
||||
</GridCol>
|
||||
<GridCol Span="23">
|
||||
<div class="chat-bubble received">
|
||||
@((MarkupString)(item.HtmlAnswers))
|
||||
@* <span class="timestamp">@item.CreateTime</span> *@
|
||||
</div>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
}
|
||||
|
||||
</Virtualize>
|
||||
</div>
|
||||
<div style="flex-shrink:0;margin:10px;">
|
||||
<AntDesign.Input @bind-Value="@(_messageInput)" DebounceMilliseconds="@(-1)" Placeholder="输入消息回车发送" OnPressEnter="@(async () => await OnSendAsync())" Disabled="@Sendding">
|
||||
<Suffix>
|
||||
<Button Icon="send" Type="@(ButtonType.Link)" OnClick="@(async () => await OnSendAsync())" Disabled="@Sendding"></Button>
|
||||
</Suffix>
|
||||
</AntDesign.Input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
width: 350px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
margin-bottom: 0;
|
||||
border-radius: 5px;
|
||||
max-width: 70%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.received {
|
||||
background-color: #f0f0f0;
|
||||
align-self: flex-start;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.sent {
|
||||
background-color: #daf8cb;
|
||||
align-self: flex-end;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: block;
|
||||
font-size: 0.75em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.received .timestamp {
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sent .timestamp {
|
||||
text-align: left;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
233
AntSK/Pages/ChatPage/OpenChat.razor.cs
Normal file
233
AntSK/Pages/ChatPage/OpenChat.razor.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
using AntDesign;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Core;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Text;
|
||||
using AntSK.Domain.Utils;
|
||||
|
||||
namespace AntSK.Pages.ChatPage
|
||||
{
|
||||
public partial class OpenChat
|
||||
{
|
||||
[Parameter]
|
||||
public string AppId { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
[Inject]
|
||||
protected IApps_Repositories _apps_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected MemoryServerless _memory { get; set; }
|
||||
[Inject]
|
||||
protected Kernel _kernel { get; set; }
|
||||
|
||||
protected bool _loading = false;
|
||||
protected List<MessageInfo> MessageList = [];
|
||||
protected string? _messageInput;
|
||||
protected string _json = "";
|
||||
protected bool Sendding = false;
|
||||
|
||||
protected Apps app = new Apps();
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
app = _apps_Repositories.GetFirst(p=>p.Id==AppId);
|
||||
}
|
||||
protected async Task OnSendAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_messageInput))
|
||||
{
|
||||
_ = Message.Info("请输入消息", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
MessageList.Add(new MessageInfo() {
|
||||
ID=Guid.NewGuid().ToString(),
|
||||
Context=_messageInput,
|
||||
CreateTime=DateTime.Now,
|
||||
IsSend=true
|
||||
});
|
||||
|
||||
|
||||
Sendding = true;
|
||||
await SendAsync(_messageInput);
|
||||
_messageInput = "";
|
||||
Sendding = false;
|
||||
|
||||
}
|
||||
protected async Task OnCopyAsync(MessageInfo item)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_messageInput = item.Context;
|
||||
});
|
||||
}
|
||||
|
||||
protected async Task OnClearAsync(string id)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
MessageList = MessageList.Where(w => w.ID != id).ToList();
|
||||
});
|
||||
}
|
||||
|
||||
protected async Task<bool> SendAsync(string questions)
|
||||
{
|
||||
string msg = questions;
|
||||
//处理多轮会话
|
||||
if (MessageList.Count > 0)
|
||||
{
|
||||
msg = await HistorySummarize(questions);
|
||||
}
|
||||
|
||||
Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
|
||||
switch (app.Type)
|
||||
{
|
||||
case "chat":
|
||||
//普通会话
|
||||
await SendChat(questions, msg, app);
|
||||
break;
|
||||
case "kms":
|
||||
//知识库问答
|
||||
await SendKms(questions, msg, app);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendKms(string questions, string msg, Apps app)
|
||||
{
|
||||
//知识库问答
|
||||
var filters = new List<MemoryFilter>();
|
||||
|
||||
var kmsidList = app.KmsIdList.Split(",");
|
||||
foreach (var kmsid in kmsidList)
|
||||
{
|
||||
filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
|
||||
}
|
||||
|
||||
var kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
var markdown = new Markdown();
|
||||
string htmlAnswers = markdown.Transform(answers);
|
||||
var info1 = new MessageInfo()
|
||||
{
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Context = answers,
|
||||
HtmlAnswers = htmlAnswers,
|
||||
CreateTime = DateTime.Now,
|
||||
};
|
||||
MessageList.Add(info1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendChat(string questions, string msg, Apps app)
|
||||
{
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$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 chatResult = _kernel.InvokeStreamingAsync<StreamingChatMessageContent>(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
MessageInfo info = null;
|
||||
var markdown = new Markdown();
|
||||
await foreach (var content in chatResult)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
info = new MessageInfo();
|
||||
info.ID = Guid.NewGuid().ToString();
|
||||
info.Context = content?.Content?.ConvertToString();
|
||||
info.HtmlAnswers = content?.Content?.ConvertToString();
|
||||
info.CreateTime = DateTime.Now;
|
||||
|
||||
MessageList.Add(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.HtmlAnswers += content.Content;
|
||||
await Task.Delay(50);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
//全部处理完后再处理一次Markdown
|
||||
info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(string questions)
|
||||
{
|
||||
if (MessageList.Count > 1)
|
||||
{
|
||||
StringBuilder history = new StringBuilder();
|
||||
foreach (var item in MessageList)
|
||||
{
|
||||
if (item.IsSend)
|
||||
{
|
||||
history.Append($"user:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Append($"assistant:{item.Context}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return questions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
AntSK/Pages/ChatPage/OpenChat.razor.css
Normal file
3
AntSK/Pages/ChatPage/OpenChat.razor.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.ant-card-body {
|
||||
height: 90% !important;
|
||||
}
|
||||
@@ -1,10 +1,27 @@
|
||||
@namespace AntSK.Pages.Exception
|
||||
@page "/exception/404"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<Result Status="404"
|
||||
Title="404"
|
||||
SubTitle="Sorry, the page you visited does not exist.">
|
||||
<Extra>
|
||||
<Button Type="primary">Back Home</Button>
|
||||
</Extra>
|
||||
</Result>
|
||||
<div class="rail">
|
||||
@for (var i = 0; i < StampCount; i++)
|
||||
{
|
||||
<div class="@((i % 2 == 0) ? "stamp zero" : "stamp four")">
|
||||
@(i % 2 == 0 ? "4" : "0")
|
||||
</div>
|
||||
}
|
||||
<div class="world">
|
||||
<div class="forward">
|
||||
<div class="box">
|
||||
@for (var i = 0; i < 6; i++)
|
||||
{
|
||||
<div class="wall"></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private int StampCount { get; set; } = 20; // 初始的循环次数
|
||||
}
|
||||
|
||||
245
AntSK/Pages/Exception/404/404.razor.css
Normal file
245
AntSK/Pages/Exception/404/404.razor.css
Normal file
@@ -0,0 +1,245 @@
|
||||
body {
|
||||
background: #fff;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
font-family: "Anton", sans-serif;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
div {
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.rail {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: rotateX(-30deg) rotateY(-50deg);
|
||||
}
|
||||
|
||||
.rail .stamp {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgb(20, 20, 20);
|
||||
color: #fff;
|
||||
font-size: 7rem;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(1) {
|
||||
animation: stampSlide 40000ms -2300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(2) {
|
||||
animation: stampSlide 40000ms -4300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(3) {
|
||||
animation: stampSlide 40000ms -6300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(4) {
|
||||
animation: stampSlide 40000ms -8300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(5) {
|
||||
animation: stampSlide 40000ms -10300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(6) {
|
||||
animation: stampSlide 40000ms -12300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(7) {
|
||||
animation: stampSlide 40000ms -14300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(8) {
|
||||
animation: stampSlide 40000ms -16300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(9) {
|
||||
animation: stampSlide 40000ms -18300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(10) {
|
||||
animation: stampSlide 40000ms -20300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(11) {
|
||||
animation: stampSlide 40000ms -22300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(12) {
|
||||
animation: stampSlide 40000ms -24300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(13) {
|
||||
animation: stampSlide 40000ms -26300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(14) {
|
||||
animation: stampSlide 40000ms -28300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(15) {
|
||||
animation: stampSlide 40000ms -30300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(16) {
|
||||
animation: stampSlide 40000ms -32300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(17) {
|
||||
animation: stampSlide 40000ms -34300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(18) {
|
||||
animation: stampSlide 40000ms -36300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(19) {
|
||||
animation: stampSlide 40000ms -38300ms linear infinite;
|
||||
}
|
||||
|
||||
.rail .stamp:nth-child(20) {
|
||||
animation: stampSlide 40000ms -40300ms linear infinite;
|
||||
}
|
||||
|
||||
@keyframes stampSlide {
|
||||
0% {
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
|
||||
}
|
||||
}
|
||||
|
||||
.world .forward {
|
||||
position: absolute;
|
||||
animation: slide 2000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
transform-origin: 100% 100%;
|
||||
animation: roll 2000ms cubic-bezier(1, 0.01, 1, 1) infinite;
|
||||
}
|
||||
|
||||
.world .box .wall {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
border: 1px solid rgb(250, 250, 250);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.world .box .wall::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 7rem;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(1) {
|
||||
transform: translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(2) {
|
||||
transform: rotateX(180deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(3) {
|
||||
transform: rotateX(90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(3)::before {
|
||||
transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms -2000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(4) {
|
||||
transform: rotateX(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(4)::before {
|
||||
transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms -2000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(5) {
|
||||
transform: rotateY(90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(5)::before {
|
||||
transform: rotateX(180deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms linear infinite;
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(6) {
|
||||
transform: rotateY(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.world .box .wall:nth-child(6)::before {
|
||||
transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
|
||||
animation: zeroFour 4000ms linear infinite;
|
||||
}
|
||||
|
||||
@keyframes zeroFour {
|
||||
0% {
|
||||
content: "4";
|
||||
}
|
||||
|
||||
100% {
|
||||
content: "0";
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes roll {
|
||||
0% {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
|
||||
85% {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
87% {
|
||||
transform: rotateZ(88deg);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-200px);
|
||||
}
|
||||
}
|
||||
29
AntSK/Pages/Index.razor
Normal file
29
AntSK/Pages/Index.razor
Normal file
@@ -0,0 +1,29 @@
|
||||
@namespace AntSK.Pages
|
||||
@using MarkdownSharp
|
||||
@page "/"
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<Body>
|
||||
@((MarkupString)(body))
|
||||
</Body>
|
||||
|
||||
@code {
|
||||
private string body = new Markdown().Transform(@"非常感谢您对**AntSK**的喜爱,您的支持对我来说是莫大的鼓励。
|
||||
|
||||
如果您对技术类视频感兴趣,欢迎关注我的B站账号,获取更多精彩内容。
|
||||
|
||||
- **Bilibili账号**:[点击这里](https://space.bilibili.com/1673184683/channel/series)
|
||||
|
||||
同时,如果您感兴趣,可以访问我的GitHub页面,查看更多开源项目和代码:
|
||||
|
||||
- **GitHub页面**:[点击这里](https://github.com/xuzeyu91)
|
||||
|
||||
另外,如果您想加入由我创建的**.Net/AI应用开发微信交流群**,欢迎先添加我的微信**(xuzeyu91)**。
|
||||
|
||||
发送“进群请求”,我将会把您邀请进群。
|
||||
|
||||
期待与您在线上交流技术,共同进步!");
|
||||
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
@using AntSK.Models
|
||||
@page "/Kms/Add"
|
||||
@inject IMessageService _message
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<PageContainer Title="新增知识库">
|
||||
<ChildContent>
|
||||
@@ -11,29 +13,21 @@
|
||||
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=" " Style="margin-top:32px" WrapperCol="LayoutModel._submitFormLayout.WrapperCol">
|
||||
<Button Type="primary" HtmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
</FormItem>
|
||||
@if ( !string.IsNullOrEmpty(_errorMsg))
|
||||
{
|
||||
<Alert Type="@AlertType.Error"
|
||||
Message="错误"
|
||||
Description="@_errorMsg"
|
||||
ShowIcon="true"
|
||||
/>
|
||||
}
|
||||
</Form>
|
||||
</Card>
|
||||
</ChildContent>
|
||||
|
||||
@@ -13,41 +13,17 @@ namespace AntSK.Pages.KmsPage
|
||||
protected IKmss_Repositories _kmss_Repositories { get; set; }
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; }
|
||||
|
||||
private string _errorMsg { get; set; }
|
||||
[Inject]
|
||||
protected MessageService? Message { get; set; }
|
||||
|
||||
private readonly Kmss _kmsModel = new Kmss() ;
|
||||
|
||||
private readonly FormItemLayout _formItemLayout = new FormItemLayout
|
||||
{
|
||||
LabelCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 7 },
|
||||
},
|
||||
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24 },
|
||||
Sm = new EmbeddedProperty { Span = 12 },
|
||||
Md = new EmbeddedProperty { Span = 10 },
|
||||
}
|
||||
};
|
||||
private readonly FormItemLayout _submitFormLayout = new FormItemLayout
|
||||
{
|
||||
WrapperCol = new ColLayoutParam
|
||||
{
|
||||
Xs = new EmbeddedProperty { Span = 24, Offset = 0 },
|
||||
Sm = new EmbeddedProperty { Span = 10, Offset = 7 },
|
||||
}
|
||||
};
|
||||
|
||||
private void HandleSubmit()
|
||||
{
|
||||
_kmsModel.Id = Guid.NewGuid().ToString();
|
||||
if (_kmss_Repositories.IsAny(p => p.Name == _kmsModel.Name))
|
||||
{
|
||||
_errorMsg = "名称已存在!";
|
||||
_ = Message.Error("名称已存在!", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@page "/Kms/Detail/{KmsID}"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<div>
|
||||
<PageContainer Title="知识库文档">
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@using AntSK.Domain.Domain.Dto
|
||||
@page "/Kms/DetailList/{KmsID}/{FileId}"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
|
||||
<Button Type="@ButtonType.Primary" OnClick="NavigateBack">返回</Button>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@using AntSK.Domain.Repositories
|
||||
@page "/KmsList"
|
||||
@inject NavigationManager NavigationManager
|
||||
@using AntSK.Services.Auth
|
||||
@inherits AuthComponentBase
|
||||
|
||||
|
||||
<PageContainer Title="知识库列表">
|
||||
@@ -24,7 +26,7 @@
|
||||
@if (string.IsNullOrEmpty(context.Id))
|
||||
{
|
||||
<Button Type="dashed" class="newButton" @onclick="NavigateToAddKms">
|
||||
<Icon Type="plus" Theme="outline" /> 创建应用
|
||||
<Icon Type="plus" Theme="outline" /> 创建知识库
|
||||
</Button>
|
||||
}
|
||||
else
|
||||
|
||||
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("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
64
AntSK/Pages/Setting/User/UserList.razor
Normal file
64
AntSK/Pages/Setting/User/UserList.razor
Normal file
@@ -0,0 +1,64 @@
|
||||
@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" />
|
||||
</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)
|
||||
)}" 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>;
|
||||
|
||||
}
|
||||
44
AntSK/Pages/Setting/User/UserList.razor.cs
Normal file
44
AntSK/Pages/Setting/User/UserList.razor.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
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 readonly IDictionary<string, ProgressStatus> _pStatus = new Dictionary<string, ProgressStatus>
|
||||
{
|
||||
{"active", ProgressStatus.Active},
|
||||
{"exception", ProgressStatus.Exception},
|
||||
{"normal", ProgressStatus.Normal},
|
||||
{"success", ProgressStatus.Success}
|
||||
};
|
||||
|
||||
private List<Users> _data;
|
||||
|
||||
private string _searchKeyword;
|
||||
|
||||
[Inject]
|
||||
protected IUsers_Repositories _users_Repositories { get; set; }
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_data = _users_Repositories.GetList();
|
||||
}
|
||||
|
||||
public void AddUser() {
|
||||
NavigationManager.NavigateTo("/setting/user/add");
|
||||
}
|
||||
|
||||
public void Edit(string userid)
|
||||
{
|
||||
NavigationManager.NavigateTo("/setting/user/add/"+userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -1,59 +1,27 @@
|
||||
@namespace AntSK.Pages.User
|
||||
@layout UserLayout
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@page "/user/login"
|
||||
|
||||
<div class="main__b__0">
|
||||
<div class="login">
|
||||
<Form Model="@_model" OnFinish="HandleSubmit">
|
||||
<Tabs ActiveKey="@context.LoginType">
|
||||
<TabPane Key="1" Tab="Account Login">
|
||||
<TabPane Key="1" Tab="账号登陆">
|
||||
<FormItem>
|
||||
<AntDesign.Input Placeholder="Username: admin or user" Size="large" @bind-Value="@context.UserName">
|
||||
<AntDesign.Input Placeholder="请输入账号" Size="large" @bind-Value="@context.UserName">
|
||||
<Prefix><Icon Type="user" /></Prefix>
|
||||
</AntDesign.Input>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<AntDesign.Input Placeholder="Password: ant.design" Size="large" @bind-Value="@context.Password" Type="password">
|
||||
<AntDesign.Input Placeholder="请输入密码" Size="large" @bind-Value="@context.Password" Type="password">
|
||||
<Prefix><Icon Type="lock" /></Prefix>
|
||||
</AntDesign.Input>
|
||||
</FormItem>
|
||||
</TabPane>
|
||||
<TabPane Key="2" Tab="Mobile Login">
|
||||
<FormItem>
|
||||
<AntDesign.Input Placeholder="Phone Number" Size="large" @bind-Value="@context.Mobile">
|
||||
<Prefix><Icon Type="mobile" /></Prefix>
|
||||
</AntDesign.Input>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Row Gutter="8">
|
||||
<AntDesign.Col Span="16">
|
||||
<AntDesign.Input Placeholder="Verification code" Size="large" @bind-Value="@context.Captcha">
|
||||
<Prefix><Icon Type="mail" /></Prefix>
|
||||
</AntDesign.Input>
|
||||
</AntDesign.Col>
|
||||
<AntDesign.Col Span="8">
|
||||
<Button Size="large" Block OnClick="GetCaptcha">Verify</Button>
|
||||
</AntDesign.Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<div>
|
||||
<Checkbox Checked="@context.AutoLogin">
|
||||
Auto Login
|
||||
</Checkbox>
|
||||
<a style="float: right;">
|
||||
Forgot Password
|
||||
</a>
|
||||
</div>
|
||||
<Button Type="primary" HtmlType="submit" Class="submit" Size="large" Block>Log in </Button>
|
||||
<div class="other">
|
||||
Other Login Methods
|
||||
<Icon Class="icon" Type="alipay-circle" />
|
||||
<Icon Class="icon" Type="taobao-circle" />
|
||||
<Icon Class="icon" Type="weibo-circle" />
|
||||
<a class="register" href="/user/register">Register Account</a>
|
||||
</div>
|
||||
<Button Type="primary" HtmlType="submit" Class="submit" Size="large" Block>登陆</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,6 +3,11 @@ using Microsoft.AspNetCore.Components;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Models;
|
||||
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
|
||||
{
|
||||
@@ -12,25 +17,21 @@ namespace AntSK.Pages.User
|
||||
|
||||
[Inject] public NavigationManager NavigationManager { get; set; }
|
||||
|
||||
[Inject] public IAccountService AccountService { get; set; }
|
||||
|
||||
[Inject] public MessageService Message { get; set; }
|
||||
|
||||
public void HandleSubmit()
|
||||
public async Task HandleSubmit()
|
||||
{
|
||||
if (_model.UserName == "admin" && _model.Password == "ant.design")
|
||||
//判断是否管理员
|
||||
var loginFailed = await((AntSKAuthProvider)AuthenticationStateProvider).SignIn(_model.UserName, _model.Password);
|
||||
if (loginFailed)
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_model.UserName == "user" && _model.Password == "ant.design") NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
public async Task GetCaptcha()
|
||||
{
|
||||
var captcha = await AccountService.GetCaptchaAsync(_model.Mobile);
|
||||
await Message.Success($"Verification code validated successfully! The verification code is: {captcha}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Message.Error("账号密码错误", 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,11 @@ using Microsoft.KernelMemory.ContentStorage.DevTools;
|
||||
using Microsoft.KernelMemory.FileSystem.DevTools;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.SemanticKernel;
|
||||
using System.Configuration;
|
||||
using Microsoft.KernelMemory.Postgres;
|
||||
using AntSK.Domain.Repositories;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.SemanticKernel.Plugins.Core;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using AntSK.Services.Auth;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
// Add services to the container.
|
||||
@@ -32,17 +33,18 @@ builder.Services.AddControllers().AddJsonOptions(config =>
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddServerSideBlazor();
|
||||
builder.Services.AddAntDesign();
|
||||
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, AntSKAuthProvider>();
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(sp.GetService<NavigationManager>()!.BaseUri)
|
||||
});
|
||||
builder.Services.Configure<ProSettings>(builder.Configuration.GetSection("ProSettings"));
|
||||
builder.Services.AddScoped<IChartService, ChartService>();
|
||||
builder.Services.AddScoped<IProjectService, ProjectService>();
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IAccountService, AccountService>();
|
||||
builder.Services.AddScoped<IProfileService, ProfileService>();
|
||||
builder.Services.AddServicesFromAssemblies("AntSK");
|
||||
builder.Services.AddServicesFromAssemblies("AntSK.Domain");
|
||||
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new() { Title = "AntSK.Api", Version = "v1" });
|
||||
@@ -72,6 +74,8 @@ 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();
|
||||
@@ -115,28 +119,32 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
//初始化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;
|
||||
});
|
||||
//Kernel Memory
|
||||
var searchClientConfig = new SearchClientConfig
|
||||
{
|
||||
MaxAskPromptSize = 128000,
|
||||
MaxAskPromptSize = 2048,
|
||||
MaxMatchesCount = 3,
|
||||
AnswerTokens = 1000,
|
||||
EmptyAnswer = "知识库未搜索到相关内容"
|
||||
@@ -149,20 +157,32 @@ void InitSK(WebApplicationBuilder builder)
|
||||
.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;
|
||||
});
|
||||
}
|
||||
void RegisterPluginsWithKernel(Kernel kernel)
|
||||
{
|
||||
kernel.ImportPluginFromObject(new ConversationSummaryPlugin(), "ConversationSummaryPlugin");
|
||||
kernel.ImportPluginFromObject(new TimePlugin(), "TimePlugin");
|
||||
}
|
||||
|
||||
|
||||
56
AntSK/Services/Auth/AntSKAuthProvider .cs
Normal file
56
AntSK/Services/Auth/AntSKAuthProvider .cs
Normal file
@@ -0,0 +1,56 @@
|
||||
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(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
|
||||
var claims = new[] { new Claim(ClaimTypes.Name, username) };
|
||||
identity = new ClaimsIdentity(claims, "AntSKAdmin");
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
return Task.FromResult(new AuthenticationState(user));
|
||||
}
|
||||
}
|
||||
}
|
||||
31
AntSK/Services/Auth/AuthComponentBase.cs
Normal file
31
AntSK/Services/Auth/AuthComponentBase.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace AntSK.Services.Auth
|
||||
{
|
||||
public class AuthComponentBase : ComponentBase
|
||||
{
|
||||
[Inject]
|
||||
public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
|
||||
[Inject]
|
||||
public NavigationManager NavigationManager { get; set; }
|
||||
public ClaimsPrincipal User { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await GetAuthenticationStateAsync();
|
||||
}
|
||||
|
||||
private async Task GetAuthenticationStateAsync()
|
||||
{
|
||||
var authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
User = authenticationState.User;
|
||||
|
||||
if (!User.Identity.IsAuthenticated)
|
||||
{
|
||||
NavigationManager.NavigateTo("/user/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
210
AntSK/Services/OpenApi/OpenApiService.cs
Normal file
210
AntSK/Services/OpenApi/OpenApiService.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Model;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Domain.Utils;
|
||||
using AntSK.Models;
|
||||
using AntSK.Models.OpenAPI;
|
||||
using AntSK.Pages.ChatPage;
|
||||
using MarkdownSharp;
|
||||
using Microsoft.KernelMemory;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Microsoft.SemanticKernel;
|
||||
using System.Text;
|
||||
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;
|
||||
|
||||
namespace AntSK.Services.OpenApi
|
||||
{
|
||||
public interface IOpenApiService
|
||||
{
|
||||
Task Chat(OpenAIModel model, string sk, HttpContext HttpContext);
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IOpenApiService), ServiceLifetime.Scoped)]
|
||||
public class OpenApiService(
|
||||
IApps_Repositories _apps_Repositories,
|
||||
IKmss_Repositories _kmss_Repositories,
|
||||
IKmsDetails_Repositories _kmsDetails_Repositories,
|
||||
Kernel _kernel,
|
||||
MemoryServerless _memory
|
||||
) : IOpenApiService
|
||||
{
|
||||
public async Task Chat(OpenAIModel model,string sk, HttpContext HttpContext)
|
||||
{
|
||||
|
||||
|
||||
Apps app = _apps_Repositories.GetFirst(p => p.SecretKey == sk);
|
||||
|
||||
if (app.IsNotNull())
|
||||
{
|
||||
string msg= await HistorySummarize(model);
|
||||
switch (app.Type)
|
||||
{
|
||||
case "chat":
|
||||
//普通会话
|
||||
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":
|
||||
//知识库问答
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task SendChatStream( HttpContext HttpContext, OpenAIStreamResult result, Apps app, string msg)
|
||||
{
|
||||
HttpContext.Response.Headers.Add("Content-Type", "text/event-stream");
|
||||
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$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 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>
|
||||
/// 发送知识库问答
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> SendKms( string msg, Apps app)
|
||||
{
|
||||
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 kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
|
||||
if (kmsResult != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kmsResult.Result))
|
||||
{
|
||||
string answers = kmsResult.Result;
|
||||
result = answers;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送普通对话
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> SendChat( string msg, Apps app)
|
||||
{
|
||||
string result = "";
|
||||
if (string.IsNullOrEmpty(app.Prompt))
|
||||
{
|
||||
//如果模板为空,给默认提示词
|
||||
app.Prompt = "{{$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 chatResult = await _kernel.InvokeAsync(function: func, arguments: new KernelArguments() { ["input"] = msg });
|
||||
if (chatResult.IsNotNull())
|
||||
{
|
||||
string answers = chatResult.GetValue<string>();
|
||||
result = answers;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 历史会话的会话总结
|
||||
/// </summary>
|
||||
/// <param name="questions"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> HistorySummarize(OpenAIModel model)
|
||||
{
|
||||
|
||||
StringBuilder history = new StringBuilder();
|
||||
string questions = model.messages[model.messages.Count-1].content;
|
||||
for(int i=0;i<model.messages.Count()-1;i++)
|
||||
{
|
||||
var item = model.messages[i];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Domain.Repositories;
|
||||
using AntSK.Models;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services
|
||||
{
|
||||
@@ -10,6 +13,7 @@ namespace AntSK.Services
|
||||
Task<string> GetCaptchaAsync(string modile);
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IAccountService), ServiceLifetime.Scoped)]
|
||||
public class AccountService : IAccountService
|
||||
{
|
||||
private readonly Random _random = new Random();
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Models;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services
|
||||
{
|
||||
@@ -12,7 +14,7 @@ namespace AntSK.Services
|
||||
Task<ChartDataItem[]> GetSalesDataAsync();
|
||||
Task<RadarDataItem[]> GetRadarDataAsync();
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IChartService), ServiceLifetime.Scoped)]
|
||||
public class ChartService : IChartService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Models;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services
|
||||
{
|
||||
@@ -10,7 +12,7 @@ namespace AntSK.Services
|
||||
Task<BasicProfileDataType> GetBasicAsync();
|
||||
Task<AdvancedProfileData> GetAdvancedAsync();
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IProfileService), ServiceLifetime.Scoped)]
|
||||
public class ProfileService : IProfileService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
@@ -2,7 +2,9 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Models;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services
|
||||
{
|
||||
@@ -13,7 +15,7 @@ namespace AntSK.Services
|
||||
Task<ListItemDataType[]> GetFakeListAsync(int count = 0);
|
||||
Task<NoticeItem[]> GetNoticesAsync();
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IProjectService), ServiceLifetime.Scoped)]
|
||||
public class ProjectService : IProjectService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntSK.Domain.Common.DependencyInjection;
|
||||
using AntSK.Models;
|
||||
using ServiceLifetime = AntSK.Domain.Common.DependencyInjection.ServiceLifetime;
|
||||
|
||||
namespace AntSK.Services
|
||||
{
|
||||
@@ -9,7 +11,7 @@ namespace AntSK.Services
|
||||
{
|
||||
Task<CurrentUser> GetCurrentUserAsync();
|
||||
}
|
||||
|
||||
[ServiceDescription(typeof(IUserService), ServiceLifetime.Scoped)]
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
@@ -27,13 +27,21 @@
|
||||
"Postgres": "Host=;Port=;Database=antsk;Username=;Password="
|
||||
},
|
||||
"OpenAIOption": {
|
||||
"EndPoint": "",
|
||||
"EndPoint": "https://localhost:5001/llama/",
|
||||
"Key": "",
|
||||
"Model": "",
|
||||
"EmbeddingModel": ""
|
||||
"Model": "gpt-3.5-turbo",
|
||||
"EmbeddingModel": "text-embedding-ada-002"
|
||||
},
|
||||
"Postgres": {
|
||||
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
|
||||
"TableNamePrefix": "km-"
|
||||
},
|
||||
"LLamaSharp": {
|
||||
"Chat": "D:\\isoftstone\\Code\\AI\\AntBlazor\\model\\tinyllama-1.1b-chat.gguf",
|
||||
"Embedding": "D:\\isoftstone\\Code\\AI\\AntBlazor\\model\\nomic-embed-text-v1.5.f32.gguf"
|
||||
},
|
||||
"Login": {
|
||||
"User": "admin",
|
||||
"Password": "xuzeyu"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
AntSK/wwwroot/assets/ai.png
Normal file
BIN
AntSK/wwwroot/assets/ai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -21,6 +21,13 @@
|
||||
"path": "/setting",
|
||||
"name": "设置",
|
||||
"key": "setting",
|
||||
"icon": "setting"
|
||||
"icon": "setting",
|
||||
"children": [
|
||||
{
|
||||
"path": "/setting/userlist",
|
||||
"name": "用户管理",
|
||||
"key": "setting.user"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
175
AntSK/wwwroot/js/iframe.js
Normal file
175
AntSK/wwwroot/js/iframe.js
Normal file
@@ -0,0 +1,175 @@
|
||||
async function embedChatbot() {
|
||||
const chatBtnId = 'aiagent-chatbot-button'
|
||||
const chatWindowId = 'aiagent-chatbot-window'
|
||||
const script = document.getElementById('antsk-iframe')
|
||||
const botSrc = script?.getAttribute('data-src')
|
||||
const width = script?.getAttribute('data-width') || '30rem'
|
||||
const height = script?.getAttribute('data-height') || '50rem'
|
||||
const primaryColor = script?.getAttribute('data-color') || '#4e83fd'
|
||||
const defaultOpen = script?.getAttribute('data-default-open') === 'true'
|
||||
const MessageIconUrl = script?.getAttribute('data-message-icon-url')
|
||||
|
||||
if (!botSrc) {
|
||||
console.error(`Can't find chaturl`)
|
||||
return
|
||||
}
|
||||
if (document.getElementById(chatBtnId)) {
|
||||
return
|
||||
}
|
||||
|
||||
const MessageIcon = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="50px" viewBox="0 0 48 50" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="编组-6" transform="translate(0.000000, 1.000000)">
|
||||
<rect id="矩形" fill="#FFFFFF" opacity="0" x="0" y="0" width="48" height="49.7454545"></rect>
|
||||
<g id="zhinengxuexi-5" transform="translate(0.000000, 1.000000)" fill-rule="nonzero">
|
||||
<path d="M39.4407295,31.1732523 L39.4407295,17.3860182 C39.4407295,16.1702128 38.7355623,15.0273556 37.6899696,14.3221884 L25.7264438,7.44072948 C24.6808511,6.83282675 23.2948328,6.83282675 22.224924,7.44072948 L10.3100304,14.3465046 C9.26443769,14.9544073 8.55927052,16.0972644 8.55927052,17.4103343 L8.55927052,31.2705167 C8.55927052,32.4863222 9.26443769,33.6291793 10.3100304,34.3343465 L22.2735562,41.1428571 C23.3191489,41.7507599 24.7051672,41.7507599 25.775076,41.1428571 L37.7386018,34.2613982 C38.8328267,33.6291793 39.4407295,32.4863222 39.4407295,31.1732523 Z" id="路径" fill="#D5ECFF"></path>
|
||||
<path d="M28.7416413,31.7082067 L24.7051672,31.7082067 L23.3920973,27.768997 L17.118541,27.768997 L15.8054711,31.7082067 L11.768997,31.7082067 L18.0425532,14.443769 L22.3465046,14.443769 L28.7416413,31.7082067 L28.7416413,31.7082067 Z M22.662614,24.8510638 L20.6930091,18.8449848 C20.5957447,18.4802432 20.4984802,17.993921 20.4012158,17.337386 L20.3039514,17.337386 C20.3039514,17.7993921 20.1094225,18.3586626 20.0121581,18.8449848 L18.0425532,24.8510638 L22.662614,24.8510638 L22.662614,24.8510638 Z M34.6504559,14.3465046 L34.6504559,31.6109422 L31.0030395,31.6109422 L31.0030395,14.3465046 L34.6504559,14.3465046 Z" id="形状" fill="#027FE6"></path>
|
||||
<path d="M44.5957447,29.0820669 C43.8905775,29.0820669 43.2826748,29.6899696 43.2826748,30.3951368 L43.2826748,34.0668693 C43.2826748,34.8449848 42.8449848,35.550152 42.1398176,35.9878419 L25.3130699,45.6656535 C24.6079027,46.1033435 23.7325228,46.1033435 23.0516717,45.6656535 L6.22492401,35.9878419 C5.51975684,35.6474164 5.08206687,34.8449848 5.08206687,34.0668693 L5.08206687,29.2036474 C6.32218845,28.6443769 7.17325228,27.4042553 7.17325228,25.9452888 C7.17325228,23.9756839 5.56838906,22.3708207 3.59878419,22.3708207 C1.62917933,22.3708207 0,23.9756839 0,25.9452888 C0,27.5258359 1.0212766,28.8632219 2.43161094,29.325228 L2.43161094,34.0425532 C2.43161094,35.7933131 3.37993921,37.3495441 4.86322188,38.224924 L21.6899696,48 C22.4680851,48.43769 23.2705167,48.6079027 24.1215805,48.6079027 C24.899696,48.6079027 25.775076,48.3404255 26.5531915,47.9027356 L43.3069909,38.224924 C44.7902736,37.3495441 45.7386018,35.7933131 45.7386018,34.0425532 L45.7386018,30.3708207 C45.8115502,29.6170213 45.2765957,29.0820669 44.5957447,29.0820669 Z M45.9088146,18.7720365 L45.9088146,14.5167173 C45.9088146,12.7659574 44.9604863,11.2097264 43.4772036,10.3343465 L26.6261398,0.656534954 C25.1428571,-0.218844985 23.2218845,-0.218844985 21.7386018,0.656534954 L4.88753799,10.3343465 C3.40425532,11.2097264 2.45592705,12.7659574 2.45592705,14.5167173 L2.45592705,17.8237082 C2.45592705,18.5288754 3.06382979,19.1367781 3.76899696,19.1367781 C4.47416413,19.1367781 5.08206687,18.5288754 5.08206687,17.8237082 L5.08206687,14.5167173 C5.08206687,13.7386018 5.51975684,13.0334347 6.22492401,12.5957447 L23.0516717,2.91793313 C23.7568389,2.48024316 24.6322188,2.48024316 25.3130699,2.91793313 L42.0668693,12.6930091 C42.7720365,13.0334347 43.2097264,13.8358663 43.2097264,14.6139818 L43.2097264,18.674772 C41.8480243,19.1854103 40.8510638,20.4741641 40.8510638,22.0303951 C40.8510638,24 42.4559271,25.6048632 44.4255319,25.6048632 C46.3951368,25.6048632 48,24 48,22.0303951 C48,20.5714286 47.1246201,19.331307 45.9088146,18.7720365 Z" id="形状" fill="#027FE6"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>`
|
||||
|
||||
const CloseIcon = `<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690535441526" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6367" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024zM305.956571 370.395429L447.488 512 305.956571 653.604571a45.568 45.568 0 1 0 64.438858 64.438858L512 576.512l141.604571 141.531429a45.568 45.568 0 0 0 64.438858-64.438858L576.512 512l141.531429-141.604571a45.568 45.568 0 1 0-64.438858-64.438858L512 447.488 370.395429 305.956571a45.568 45.568 0 0 0-64.438858 64.438858z" fill=${primaryColor} p-id="6368"></path></svg>`
|
||||
const MessageIconImg = document.createElement('img')
|
||||
MessageIconImg.src = MessageIconUrl
|
||||
MessageIconImg.style.cssText = 'width: 100%; height: 100%;'
|
||||
const ChatBtn = document.createElement('div')
|
||||
ChatBtn.id = chatBtnId
|
||||
ChatBtn.style.cssText = 'position: fixed; bottom: 1rem; right: 1rem; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;'
|
||||
|
||||
const ChatBtnDiv = document.createElement('div')
|
||||
if (MessageIconUrl) {
|
||||
ChatBtnDiv.appendChild(MessageIconImg)
|
||||
} else {
|
||||
ChatBtnDiv.innerHTML = MessageIcon
|
||||
}
|
||||
/**
|
||||
* 添加样式
|
||||
*/
|
||||
// 创建一个style元素
|
||||
const style = document.createElement('style')
|
||||
style.type = 'text/css'
|
||||
|
||||
// 添加CSS规则到style元素
|
||||
const rules = document.createTextNode('.resizing-iframe iframe { pointer-events: none; }' + '.resizing-iframe { user-select: none; }')
|
||||
|
||||
// 将规则附加到style元素
|
||||
style.appendChild(rules)
|
||||
|
||||
// 将style元素添加到document的head中,使其生效
|
||||
document.head.appendChild(style)
|
||||
const parentDiv = document.createElement('div')
|
||||
parentDiv.style.cssText = `border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 4rem; right: 1rem; min-width:${width}; min-height: ${height};width:${width}; height: ${height}; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;`
|
||||
parentDiv.id = chatWindowId
|
||||
parentDiv.style.visibility = defaultOpen ? 'unset' : 'hidden'
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.allow = 'fullscreen;microphone'
|
||||
iframe.title = 'AIAgent Chat Window'
|
||||
iframe.target = '_self'
|
||||
// iframe.id = chatWindowId
|
||||
iframe.src = botSrc
|
||||
iframe.style.cssText = `width: 100%; height: 100%; border: none;`
|
||||
parentDiv.appendChild(iframe)
|
||||
document.body.appendChild(parentDiv)
|
||||
let resizing = false
|
||||
let startX, startWidth
|
||||
|
||||
const resizer = document.createElement('div')
|
||||
resizer.className = 'resizer'
|
||||
iframe.parentElement.appendChild(resizer) // 假设iframe和resizer有相同的父元素
|
||||
|
||||
// 为resizer添加样式
|
||||
resizer.style.width = '2px'
|
||||
resizer.style.height = '100%'
|
||||
resizer.style.background = '#f8f9fd'
|
||||
resizer.style.position = 'absolute'
|
||||
resizer.style.left = '0'
|
||||
resizer.style.bottom = '0'
|
||||
resizer.style.cursor = 'e-resize'
|
||||
|
||||
// 监听鼠标按下事件
|
||||
resizer.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault()
|
||||
document.body.classList.add('resizing-iframe')
|
||||
startX = e.clientX
|
||||
// startY = e.clientY
|
||||
startWidth = parseInt(document.defaultView.getComputedStyle(parentDiv).width, 10)
|
||||
// startHeight = parseInt(document.defaultView.getComputedStyle(parentDiv).height, 10)
|
||||
resizing = true
|
||||
})
|
||||
|
||||
// 监听鼠标移动事件
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (!resizing) return
|
||||
requestAnimationFrame(() => {
|
||||
let newWidth = startWidth + (startX - e.clientX)
|
||||
// let newHeight = startHeight + (e.clientY - startY)
|
||||
parentDiv.style.width = newWidth + 'px'
|
||||
// parentDiv.style.height = newHeight + 'px'
|
||||
})
|
||||
})
|
||||
|
||||
// 监听鼠标释放事件
|
||||
document.addEventListener('mouseup', (e) => {
|
||||
document.body.classList.remove('resizing-iframe')
|
||||
resizing = false
|
||||
})
|
||||
|
||||
let chatBtnDragged = false
|
||||
let chatBtnDown = false
|
||||
let chatBtnMouseX
|
||||
let chatBtnMouseY
|
||||
ChatBtn.addEventListener('click', function () {
|
||||
if (chatBtnDragged) {
|
||||
chatBtnDragged = false
|
||||
return
|
||||
}
|
||||
const chatWindow = document.getElementById(chatWindowId)
|
||||
|
||||
if (!chatWindow) return
|
||||
const visibilityVal = chatWindow.style.visibility
|
||||
if (visibilityVal === 'hidden') {
|
||||
chatWindow.style.visibility = 'unset'
|
||||
ChatBtnDiv.innerHTML = CloseIcon
|
||||
} else {
|
||||
chatWindow.style.visibility = 'hidden'
|
||||
if (MessageIconUrl) {
|
||||
ChatBtnDiv.innerHTML = ''
|
||||
ChatBtnDiv.appendChild(MessageIconImg)
|
||||
} else {
|
||||
ChatBtnDiv.innerHTML = MessageIcon
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ChatBtn.addEventListener('mousedown', (e) => {
|
||||
if (!chatBtnMouseX && !chatBtnMouseY) {
|
||||
chatBtnMouseX = e.clientX
|
||||
chatBtnMouseY = e.clientY
|
||||
}
|
||||
|
||||
chatBtnDown = true
|
||||
})
|
||||
ChatBtn.addEventListener('mousemove', (e) => {
|
||||
if (!chatBtnDown) return
|
||||
chatBtnDragged = true
|
||||
const transformX = e.clientX - chatBtnMouseX
|
||||
const transformY = e.clientY - chatBtnMouseY
|
||||
|
||||
ChatBtn.style.transform = `translate3d(${transformX}px, ${transformY}px, 0)`
|
||||
|
||||
e.stopPropagation()
|
||||
})
|
||||
ChatBtn.addEventListener('mouseup', (e) => {
|
||||
chatBtnDown = false
|
||||
})
|
||||
ChatBtn.addEventListener('mouseleave', (e) => {
|
||||
chatBtnDown = false
|
||||
})
|
||||
|
||||
ChatBtn.appendChild(ChatBtnDiv)
|
||||
document.body.appendChild(ChatBtn)
|
||||
}
|
||||
document.body.onload = embedChatbot
|
||||
@@ -113,7 +113,7 @@ Let's see the effect
|
||||
|
||||
## How do I get started?
|
||||
|
||||
|
||||
Login is the default login account and password
|
||||
|
||||
Here I use Postgres as data storage and vector storage, because both the Semantic Kernel and Kernel Memory support it. Of course, you can switch to other ones.
|
||||
|
||||
@@ -147,6 +147,10 @@ Postgres:{
|
||||
|
||||
"TableNamePrefix": "km -"
|
||||
|
||||
},
|
||||
"Login": {
|
||||
"User": "admin",
|
||||
"Password": "xuzeyu"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
27
README.md
27
README.md
@@ -16,7 +16,7 @@
|
||||
|
||||
- **GPTs 生成**:此平台支持创建个性化的GPT模型,尝试构建您自己的GPT模型。
|
||||
|
||||
- **API接口发布**:将内部功能以API的形式对外提供,便于开发者将Xzy.AntSK.KnowledgeBase 集成进其他应用,增强应用智慧。
|
||||
- **API接口发布**:将内部功能以API的形式对外提供,便于开发者将AntSK 集成进其他应用,增强应用智慧。
|
||||
|
||||
## 应用场景
|
||||
|
||||
@@ -61,7 +61,10 @@ AntSK 适用于多种业务场景,例如:
|
||||
|
||||
在这里我使用的是Postgres 作为数据存储和向量存储,因为Semantic Kernel和Kernel Memory都支持他,当然你也可以换成其他的。
|
||||
模型默认支持openai,如果需要使用azure openai需要调整SK的依赖注入,也可以使用one-api进行集成。
|
||||
Login是默认的登陆账号和密码
|
||||
需要配置如下的配置文件
|
||||
### 需要注意的是PostgreSQL需要安装扩展vector
|
||||
|
||||
```
|
||||
"ConnectionStrings": {
|
||||
"Postgres": "Host=;Port=;Database=antsk;Username=;Password="
|
||||
@@ -75,10 +78,32 @@ AntSK 适用于多种业务场景,例如:
|
||||
"Postgres": {
|
||||
"ConnectionString": "Host=;Port=;Database=antsk;Username=;Password=",
|
||||
"TableNamePrefix": "km-"
|
||||
},
|
||||
"Login": {
|
||||
"User": "admin",
|
||||
"Password": "xuzeyu"
|
||||
}
|
||||
```
|
||||
我使用的是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为本地:
|
||||
```
|
||||
"OpenAIOption": {
|
||||
"EndPoint": "https://ip:port/llama/",
|
||||
"Key": "",
|
||||
"Model": "",
|
||||
"EmbeddingModel": ""
|
||||
},
|
||||
```
|
||||
|
||||
|
||||
想了解更多信息或开始使用 **AntSK**,可以关注我的公众号以及加入交流群。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user