Compare commits

...

5 Commits
0.2.0 ... 0.2.1

Author SHA1 Message Date
zyxucp
adbecb3b25 update 更新docker compose 2024-03-16 00:23:16 +08:00
zyxucp
277aacc34d add 动态添加dll 2024-03-15 23:53:53 +08:00
zyxucp
dba98f7968 add 增加上传dll功能 2024-03-15 22:55:23 +08:00
zyxucp
a2b0f3f3c2 add 插件导入 2024-03-15 22:45:36 +08:00
zyxucp
b5e527afdb add 增加函数列表 2024-03-15 22:33:39 +08:00
22 changed files with 538 additions and 7 deletions

View File

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

View File

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

View File

@@ -247,6 +247,11 @@
API调用秘钥
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.Funs.Path">
<summary>
接口描述
</summary>
</member>
<member name="P:AntSK.Domain.Repositories.KmsDetails.FileName">
<summary>
文件名称

View File

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

View File

@@ -1,8 +1,10 @@
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Loader;
using System.Xml;
using AntSK.Domain.Common;
using AntSK.Domain.Utils;
using System.Text.RegularExpressions;
namespace AntSK.Domain.Domain.Service
{
@@ -12,7 +14,8 @@ namespace AntSK.Domain.Domain.Service
private readonly Dictionary<string, (string Description, (Type ParameterType, string Description) ReturnType, (string ParameterName, Type ParameterType, string Description)[] Parameters)> _methodInfos;
private readonly IServiceProvider _serviceProvider;
private readonly Assembly[] _assemblies;
private Assembly[] _assemblies;
private readonly AssemblyLoadContext loadContext;
public FunctionService(IServiceProvider serviceProvider, Assembly[] assemblies)
{
@@ -20,6 +23,7 @@ namespace AntSK.Domain.Domain.Service
_methodInfos = [];
_serviceProvider = serviceProvider;
_assemblies = assemblies;
loadContext = new AssemblyLoadContext("AntSKLoadContext", true);
}
public Dictionary<string, MethodInfo> Functions => _methodCache;
@@ -48,11 +52,29 @@ namespace AntSK.Domain.Domain.Service
}
}
//动态加载部分
var loadedAssemblies = loadContext.Assemblies.ToList();
foreach (var assembly in loadedAssemblies)
{
// 从缓存中获取标记了ActionAttribute的方法
foreach (var type in assembly.GetTypes())
{
markedMethods.AddRange(type.GetMethods().Where(m =>
{
DescriptionAttribute da = (DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
return da != null && da.Description == "AntSK";
}));
}
}
// 构建方法调用
foreach (var method in markedMethods)
{
var key = $"{method.DeclaringType.Assembly.GetName().Name}_{method.DeclaringType.Name}_{method.Name}";
_methodCache.TryAdd(key, method);
string pattern = "[^a-zA-Z0-9_]";
// 使用 '-' 替换非ASCII的正则表达式的字符
key = Regex.Replace(key, pattern, "_");
_methodCache.TryAdd(key, method);
var xmlCommentHelper = new XmlCommentHelper();
xmlCommentHelper.LoadAll();
@@ -63,8 +85,44 @@ namespace AntSK.Domain.Domain.Service
var parameters = method.GetParameters().Select(x => (x.Name, x.ParameterType, dict[x.Name])).ToArray();
var returnType = xmlCommentHelper.GetMethodReturnComment(method);
if (string.IsNullOrEmpty(description))
{
description = "导入插件";
}
_methodInfos.TryAdd(key, (description, (method.ReflectedType, returnType), parameters));
}
}
public void FuncLoad(string pluginPath)
{
try
{
if (File.Exists(pluginPath))
{
string directory = Path.GetDirectoryName(pluginPath);
string fileName = Path.GetFileName(pluginPath);
var resolver = new AssemblyDependencyResolver(directory);
// Create a custom AssemblyLoadContext
loadContext.Resolving += (context, assemblyName) =>
{
string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return context.LoadFromAssemblyPath(assemblyPath);
}
return null;
};
// Load your assembly
Assembly pluginAssembly = loadContext.LoadFromAssemblyPath(pluginPath);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
}
}

View File

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

View File

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

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

View File

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

View File

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

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

@@ -0,0 +1,24 @@
using System.ComponentModel;
namespace AntSK.Test
{
/// <summary>
/// 测试插件导入
/// </summary>
public class TestFunctionImport
{
/// <summary>
/// 获取名称
/// </summary>
/// <returns>返回名称</returns>
[Description("AntSK")]
public string GetName()
{
return $"""
我的名字是AntSK,
我的作者是许泽宇
我是一个AI 知识库/智能体项目
""";
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,130 @@
using AntDesign;
using AntSK.Domain.Domain.Model.Fun;
using AntSK.Domain.Domain.Service;
using AntSK.Domain.Repositories;
using AntSK.Models;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.SemanticKernel;
namespace AntSK.Pages.ApiPage
{
public partial class FunList
{
private FunDto[] _data = { };
[Inject]
FunctionService _functionService { get; set; }
[Inject]
IServiceProvider _serviceProvider { get; set; }
[Inject]
IConfirmService _confirmService { get; set; }
[Inject]
IFuns_Repositories _funs_Repositories { get; set; }
[Inject]
protected MessageService? _message { get; set; }
bool _fileVisible = false;
bool _fileConfirmLoading = false;
List<FileInfoModel> fileList = new List<FileInfoModel>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await InitData("");
}
private async Task InitData(string searchKey)
{
var list = new List<FunDto> { new FunDto() };
_functionService.SearchMarkedMethods();
using var scope = _serviceProvider.CreateScope();
var funList = _functionService.Functions;
if (!string.IsNullOrEmpty(searchKey))
{
funList = funList.Where(x => x.Key.Contains(searchKey)).ToDictionary(x => x.Key, x => x.Value);
}
foreach (var func in funList)
{
var methodInfo = _functionService.MethodInfos[func.Key];
list.Add(new FunDto() { Name = func.Key, Description = methodInfo.Description });
}
_data = list.ToArray();
await InvokeAsync(StateHasChanged);
}
private void NavigateToAddApp()
{
NavigationManager.NavigateTo("/plugins/fun/add");
}
private async Task Search(string searchKey)
{
await InitData(searchKey);
}
private async Task AddFun() {
_fileVisible = true;
}
private async Task FileHandleOk(MouseEventArgs e)
{
try
{
foreach (var file in fileList)
{
_funs_Repositories.Insert(new Funs() { Id = Guid.NewGuid().ToString(), Path = file.FilePath });
_functionService.FuncLoad(file.FilePath);
}
_message.Info("上传成功");
await InitData("");
_fileVisible = false;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}
private void FileHandleCancel(MouseEventArgs e)
{
_fileVisible = false;
}
private void FileShowModal()
{
_fileVisible = true;
}
bool BeforeUpload(UploadFileItem file)
{
if (file.Ext != ".dll")
{
_message.Error("请上传dll文件!");
}
var IsLt500K = file.Size < 1024 * 1024 * 100;
if (!IsLt500K)
{
_message.Error("文件需不大于100MB!");
}
return IsLt500K;
}
private void OnSingleCompleted(UploadInfo fileinfo)
{
if (fileinfo.File.State == UploadState.Success)
{
//文件列表
fileList.Add(new FileInfoModel()
{
FileName = fileinfo.File.FileName,
FilePath = fileinfo.File.Url = fileinfo.File.Response
});
}
}
}
}

View File

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

View File

@@ -44,7 +44,7 @@ builder.Services.AddScoped(sp => new HttpClient
builder.Services.Configure<ProSettings>(builder.Configuration.GetSection("ProSettings"));
builder.Services.AddServicesFromAssemblies("AntSK");
builder.Services.AddServicesFromAssemblies("AntSK.Domain");
builder.Services.AddSingleton(sp => new FunctionService(sp, [typeof(AntSK.App).Assembly, typeof(AntSK.Domain.Common.AntSkFunctionAttribute).Assembly]));
builder.Services.AddSingleton(sp => new FunctionService(sp, [typeof(AntSK.App).Assembly]));
builder.Services.AddScoped<FunctionTest>();
builder.Services.AddSwaggerGen(c =>
@@ -111,6 +111,7 @@ if (!app.Environment.IsDevelopment())
app.UseStaticFiles();
InitDB(app);
LoadFun(app);
app.UseRouting();
@@ -140,7 +141,30 @@ void InitDB(WebApplication app)
_repository.GetDB().CodeFirst.InitTables(typeof(Users));
_repository.GetDB().CodeFirst.InitTables(typeof(Apis));
_repository.GetDB().CodeFirst.InitTables(typeof(AIModels));
_repository.GetDB().CodeFirst.InitTables(typeof(Funs));
//创建vector插件如果数据库没有则需要提供支持向量的数据库
_repository.GetDB().Ado.ExecuteCommandAsync($"CREATE EXTENSION IF NOT EXISTS vector;");
}
}
void LoadFun(WebApplication app)
{
try
{
using (var scope = app.Services.CreateScope())
{
//codefirst 创建表
var funRep = scope.ServiceProvider.GetRequiredService<IFuns_Repositories>();
var functionService = scope.ServiceProvider.GetRequiredService<FunctionService>();
var funs= funRep.GetList();
foreach (var fun in funs)
{
functionService.FuncLoad(fun.Path);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + " ---- " + ex.StackTrace);
}
}

View File

@@ -27,6 +27,11 @@
"path": "/plugins/apilist",
"name": "Api管理",
"key": "plugins.apilist"
},
{
"path": "/plugins/funlist",
"name": "函数管理",
"key": "plugins.funlist"
}
]
},