fix(llm): 将自定义接入LLM模式删除,全部改成微软官方接入方式

This commit is contained in:
PZ688 2025-06-19 05:03:58 +08:00
commit dd43e3a7a7
68 changed files with 1983 additions and 1530 deletions

View File

@ -156,5 +156,36 @@
}
}
]
},
"LLMCustom": {
"LLMType": "openrouter",
"ApiKey": "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",
"BaseUrl": "https://openrouter.ai/api/v1/chat/completions",
"InitSystemChatMessage": "你是一个经验丰富的AI助手请根据用户的问题给出最准确的回答,每个回答都以markdown格式输出",
"InitSystemPromptMessage": "你是一个经验丰富的AI助手请根据用户的问题给出最准确的回答",
"CanUserSwitchLLM": false,
"ModelProvider": "DeepSeek: R1",
"MaxHistory": 10,
"Timeout": 30,
"IsUserProxy": true,
"ProxyUrl": "http://127.0.0.1:10809",
"SupportLLMList": [
{
"Desciption": "OpenAI: GPT-3.5 Turbo",
"Model": "openai/gpt-3.5-turbo"
},
{
"Desciption": "Google: LearnLM 1.5 Pro",
"Model": "google/learnlm-1.5-pro-experimental:free"
},
{
"Desciption": "Meta: Llama 3.2 11B",
"Model": "meta-llama/llama-3.2-11b-vision-instruct:free"
},
{
"Desciption": "DeepSeek: R1",
"Model": "deepseek/deepseek-r1-distill-llama-70b:free"
}
]
}
}

View File

@ -34,7 +34,7 @@
},
"TableSettings": {
"EnableInitTable": true, //
"EnableIncreTable": true // [IncreTable]
"EnableIncreTable": false // [IncreTable]
},
"SeedSettings": {
"EnableInitSeed": true, //

View File

@ -16,13 +16,11 @@ global using Mapster;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.SemanticKernel;
global using SqlSugar;
global using System;
global using System.Collections.Generic;
global using System.ComponentModel;
global using System.ComponentModel.DataAnnotations;
global using System.Threading.Tasks;
global using System.Linq;
global using Microsoft.SemanticKernel;
global using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
global using System.Linq;

View File

@ -1,5 +1,8 @@
using Admin.NET.Core.Ai.Interface;
using Admin.NET.Core.Ai.Models;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Application.Service.LLM;
@ -7,10 +10,12 @@ namespace Admin.NET.Application.Service.LLM;
public class LLMChangeModelTestService : IDynamicApiController, ITransient
{
private readonly ILLMFactory _llmFactory;
public LLMChangeModelTestService(ILLMFactory llmFactory)
{
_llmFactory = llmFactory;
}
/// <summary>
/// 演示大模型的使用,可以切换模型。
/// 例如可以切换到不同的模型OpenAI、Azure OpenAI、Google Gemini等。
@ -25,5 +30,4 @@ public class LLMChangeModelTestService : IDynamicApiController, ITransient
var result = await kernel.InvokePromptAsync("请介绍自己");
return result.ToString();
}
}
}

View File

@ -1,5 +1,8 @@
using Microsoft.SemanticKernel.ChatCompletion;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Application.Service.LLM;
@ -7,6 +10,7 @@ namespace Admin.NET.Application.Service.LLM;
public class LLMTestService : IDynamicApiController, ITransient
{
private readonly Kernel _kernel;
public LLMTestService(Kernel kernel)
{
_kernel = kernel;

View File

@ -28,9 +28,9 @@
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" Aliases="BouncyCastleV2" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.0.6" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.87" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.87" />
<PackageReference Include="Furion.Pure" Version="4.9.7.87" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.88" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.88" />
<PackageReference Include="Furion.Pure" Version="4.9.7.88" />
<PackageReference Include="Hardware.Info" Version="101.0.1.1" />
<PackageReference Include="Hashids.net" Version="1.7.0" />
<PackageReference Include="IPTools.China" Version="1.6.0" />
@ -59,15 +59,15 @@
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1259" />
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="microsoft.semantickernel" Version="1.54.0" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.Core" Version="1.54.0" />
<PackageReference Include="microsoft.semantickernel" Version="1.57.0" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.Core" Version="1.57.0" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Amazon" Version="1.56.0-alpha" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.54.0-alpha" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.HuggingFace" Version="1.56.0-preview" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.54.0-alpha" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.54.0-preview" />
<PackageReference Include="Microsoft.SemanticKernel.PromptTemplates.Handlebars" Version="1.54.0" />
<PackageReference Include="Microsoft.SemanticKernel.Yaml" Version="1.54.0" />
<PackageReference Include="Microsoft.SemanticKernel.PromptTemplates.Handlebars" Version="1.57.0" />
<PackageReference Include="Microsoft.SemanticKernel.Yaml" Version="1.57.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">

View File

@ -1,12 +1,17 @@
namespace Admin.NET.Core.Ai.Entitys;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 聊天历史
/// 聊天历史
/// </summary>
[SugarTable("LLMChatHistory")]
[IncreTable]
[SugarIndex("index_UserId_{table}", nameof(UserId), OrderByType.Asc)]
[SugarIndex("index_SummaryId_{table}", nameof(SummaryId), OrderByType.Asc)]
[SugarTable(null, "AI聊天历史表")]
[SugarIndex("i_{table}_u", nameof(UserId), OrderByType.Asc)]
[SugarIndex("i_{table}_s", nameof(SummaryId), OrderByType.Asc)]
public class LLMChatHistory : EntityBaseId
{
/// <summary>
@ -44,4 +49,4 @@ public class LLMChatHistory : EntityBaseId
/// </summary>
[SugarColumn(ColumnDescription = "创建时间")]
public long UtcCreateTime { get; set; } = DateTime.UtcNow.ToLong();
}
}

View File

@ -0,0 +1,46 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 聊天摘要历史表
/// </summary>
[SugarTable(null, "AI聊天摘要历史表")]
[SugarIndex("i_{table}_us", nameof(UserId), OrderByType.Asc)]
[SugarIndex("i_{table}_un", nameof(UniqueToken), OrderByType.Asc)]
public class LLMChatSummaryHistory : EntityBaseId
{
/// <summary>
/// 用户ID
/// </summary>
[SugarColumn(ColumnDescription = "用户ID")]
public long UserId { get; set; }
/// <summary>
/// 唯一标识
/// </summary>
[SugarColumn(ColumnDescription = "唯一标识")]
public string UniqueToken { get; set; }
/// <summary>
/// 摘要
/// </summary>
[SugarColumn(ColumnDescription = "摘要", Length = 4000)]
public string? Summary { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(ColumnDescription = "创建时间")]
public long UtcCreateTime { get; set; } = DateTime.UtcNow.ToLong();
/// <summary>
/// 聊天历史
/// </summary>
[Navigate(NavigateType.OneToMany, nameof(LLMChatHistory.SummaryId))]
public List<LLMChatHistory>? Histories { get; set; }
}

View File

@ -1,40 +0,0 @@
namespace Admin.NET.Core.Ai.Entitys;
/// <summary>
/// 聊天摘要历史
/// </summary>
[SugarTable("LLMChatSummaryHistory")]
[IncreTable]
[SugarIndex("index_UserId_{table}", nameof(UserId), OrderByType.Asc)]
[SugarIndex("index_UniqueToken_{table}", nameof(UniqueToken), OrderByType.Asc)]
public class LLMChatSummaryHistory:EntityBaseId
{
/// <summary>
/// 用户ID
/// </summary>
[SugarColumn(ColumnDescription = "用户ID")]
public long UserId { get; set; }
/// <summary>
/// 唯一标识
/// </summary>
[SugarColumn(ColumnDescription = "唯一标识")]
public string UniqueToken {get;set;}
/// <summary>
/// 摘要
/// </summary>
[SugarColumn(ColumnDescription = "摘要",Length = 4000)]
public string? Summary { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(ColumnDescription = "创建时间")]
public long UtcCreateTime {get;set;} = DateTime.UtcNow.ToLong();
/// <summary>
/// 聊天历史
/// </summary>
[Navigate(NavigateType.OneToMany, nameof(LLMChatHistory.SummaryId))]
public List<LLMChatHistory>? Histories { get; set; }
}

View File

@ -0,0 +1,43 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 保存数据库的动作枚举
/// </summary>
public enum ChatActionEnum
{
/// <summary>
/// 无
/// </summary>
None = 0,
/// <summary>
/// 追加
/// </summary>
Append,
/// <summary>
/// 追加明细
/// </summary>
AppendItem,
/// <summary>
/// 删除所有
/// </summary>
DeleteAll,
/// <summary>
/// 删除指定明细
/// </summary>
DeleteItem,
/// <summary>
/// 重命名摘要
/// </summary>
RenameSummary,
}

View File

@ -1,32 +0,0 @@
namespace Admin.NET.Core.Ai.Enums;
/// <summary>
/// 保存数据库的动作枚举
/// </summary>
public enum ChatActionEnums
{
/// <summary>
/// 无
/// </summary>
None = 0,
/// <summary>
/// 追加
/// </summary>
Append,
/// <summary>
/// 追加明细
/// </summary>
AppendItem,
/// <summary>
/// 删除所有
/// </summary>
DeleteAll,
/// <summary>
/// 删除指定明细
/// </summary>
DeleteItem,
/// <summary>
/// 重命名摘要
/// </summary>
RenameSummary,
}

View File

@ -0,0 +1,23 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core.Ai.Service;
namespace Admin.NET.Core;
public static class LLMFactoryExtention
{
/// <summary>
/// 注册LLM模型工厂
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddLLMFactory(this IServiceCollection services)
{
services.AddTransient<ILLMFactory, ChangeModelFactory>();
return services;
}
}

View File

@ -1,20 +1,10 @@
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
using Microsoft.SemanticKernel.Connectors.Ollama;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Admin.NET.Core.Ai.Option;
using System.Net;
using Admin.NET.Core.Ai.Handlers;
using OllamaSharp;
using Admin.NET.Core.Ai.Interface;
using Admin.NET.Core.Ai.Models;
namespace Admin.NET.Core.Ai.Extentions;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// LLM注册扩展类
@ -47,4 +37,4 @@ public static class LLMRegisterExtention
});
return services;
}
}
}

View File

@ -1,21 +0,0 @@
using Admin.NET.Core.Ai.Interface;
using Admin.NET.Core.Ai.Services;
using Admin.NET.Core.Ai.Services.Infrastructure;
namespace Admin.NET.Core.Ai.Extentions;
public static class LLMFactoryExtention
{
/// <summary>
/// 注册LLM模型工厂
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddLLMFactory(this IServiceCollection services)
{
services.AddTransient<ILLMFactory, ChangeModelFactory>();
return services;
}
}

View File

@ -0,0 +1,26 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using System.Net.Http.Headers;
namespace Admin.NET.Core;
public class LLMDelegatingHandler : DelegatingHandler
{
private readonly LLMCustomOptions _options;
public LLMDelegatingHandler(IOptions<LLMCustomOptions> options)
{
_options = options.Value;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//设置header
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.ApiKey);
return await base.SendAsync(request, cancellationToken);
}
}

View File

@ -1,14 +1,18 @@
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Handlers;
using System.Text.Encodings.Web;
using System.Text.Json;
namespace Admin.NET.Core;
public class LoggingHandler : DelegatingHandler
{
private readonly ILogger<LoggingHandler> _logger;
public LoggingHandler(HttpMessageHandler innerHandler)
{
InnerHandler = innerHandler ?? new HttpClientHandler();
@ -143,6 +147,7 @@ public class LoggingStream : Stream
public override bool CanSeek => _innerStream.CanSeek;
public override bool CanWrite => _innerStream.CanWrite;
public override long Length => _innerStream.Length;
public override long Position
{
get => _innerStream.Position;
@ -188,8 +193,11 @@ public class LoggingStream : Stream
}
public override void Flush() => _innerStream.Flush();
public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
public override void SetLength(long value) => _innerStream.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => _innerStream.Write(buffer, offset, count);
protected override void Dispose(bool disposing)

View File

@ -1,6 +1,10 @@
using Admin.NET.Core.Ai.Models;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Interface;
namespace Admin.NET.Core;
/// <summary>
/// LLM工厂接口
@ -13,5 +17,4 @@ public interface ILLMFactory
/// <param name="modelInput">模型输入</param>
/// <returns></returns>
Kernel CreateKernel(LLMModelInput modelInput);
}
}

View File

@ -1,6 +1,12 @@
// 1. 定义接口
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using System.Threading.Channels;
namespace Admin.NET.Core.Ai.Interface;
namespace Admin.NET.Core;
/// <summary>
/// SSE通道管理接口
@ -11,11 +17,13 @@ public interface ISseChannelManager
/// 注册
/// </summary>
ChannelReader<string> Register(long userId);
/// <summary>
/// 注销
/// </summary>
/// <param name="userId"></param>
void Unregister(long userId);
/// <summary>
/// 发送消息
/// </summary>

View File

@ -0,0 +1,32 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// LLM请求体
/// </summary>
public class LLMInputBody
{
public string Model { get; set; }
public List<LLMInputMessage> Messages { get; set; }
}
/// <summary>
/// LLM请求体消息
/// </summary>
public class LLMInputMessage
{
/// <summary>
/// 角色system、user、assistant
/// </summary>
public string Role { get; set; }
/// <summary>
/// 内容
/// </summary>
public string Content { get; set; }
}

View File

@ -0,0 +1,24 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// LLM模型输入
/// 用途:切换模型
/// </summary>
public class LLMModelInput
{
/// <summary>
/// 产品名称
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// 模型ID
/// </summary>
public string ModelId { get; set; }
}

View File

@ -1,11 +1,18 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Newtonsoft.Json;
namespace Admin.NET.Core.Ai.Models;
namespace Admin.NET.Core;
/// <summary>
/// LLM输出
/// </summary>
public class LLMOutput {
public class LLMOutput
{
public string Id { get; set; }
public string Provider { get; set; }
public string Model { get; set; }
@ -18,9 +25,11 @@ public class LLMOutput {
/// <summary>
/// LLM输出机会
/// </summary>
public class ChoicesItem {
public class ChoicesItem
{
[JsonProperty("logprobs")]
public string? Logprobs { get; set; }
public string? FinishReason { get; set; }
public string? NativeFinishReason { get; set; }
public int Index { get; set; }
@ -30,10 +39,12 @@ public class ChoicesItem {
/// <summary>
/// LLM输出消息
/// </summary>
public class OutPutMessage {
public class OutPutMessage
{
public string Role { get; set; }
public string Content { get; set; }
public Object Refusal { get; set; }
[JsonProperty("reasoning")]
public string? Reasoning { get; set; }
}
@ -41,10 +52,9 @@ public class OutPutMessage {
/// <summary>
/// LLM的消耗
/// </summary>
public class Usage {
public class Usage
{
public int PromptTokens { get; set; }
public int CompletionTokens { get; set; }
public int TotalTokens { get; set; }
}
}

View File

@ -1,25 +0,0 @@
namespace Admin.NET.Core.Ai.Models;
/// <summary>
/// LLM请求体
/// </summary>
public class LLMInputBody {
public string Model { get; set; }
public List<LLMInputMessage> Messages { get; set; }
}
/// <summary>
/// LLM请求体消息
/// </summary>
public class LLMInputMessage {
/// <summary>
/// 角色system、user、assistant
/// </summary>
public string Role { get; set; }
/// <summary>
/// 内容
/// </summary>
public string Content { get; set; }
}

View File

@ -1,18 +0,0 @@
namespace Admin.NET.Core.Ai.Models;
/// <summary>
/// LLM模型输入
/// 用途:切换模型
/// </summary>
public class LLMModelInput
{
/// <summary>
/// 产品名称
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// 模型ID
/// </summary>
public string ModelId { get; set; }
}

View File

@ -0,0 +1,35 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 手动实现LLM接口配置选项
/// </summary>
public class LLMCustomOptions : IConfigurableOptions
{
public string LLMType { get; set; } = "openrouter";
public string ApiKey { get; set; }
public string BaseUrl { get; set; } = "https://openrouter.ai/api/v1/chat/completions";
public string InitSystemChatMessage { get; set; } = "你是一个经验丰富的AI助手请根据用户的问题给出最准确的回答,每个回答都以markdown格式输出";
public string InitSystemPromptMessage { get; set; } = "你是一个经验丰富的AI助手请根据用户的问题给出最准确的回答";
public bool CanUserSwitchLLM { get; set; } = false;
public string ModelProvider { get; set; }
public int MaxHistory { get; set; } = 10;
public bool IsUserProxy { get; set; } = false;
public string ProxyUrl { get; set; } = "";
public int Timeout { get; set; } = 30;
public List<LLMItem> SupportLLMList { get; set; }
}
/// <summary>
/// LLM配置选项
/// </summary>
public class LLMItem
{
public string Desciption { get; set; }
public string Model { get; set; }
}

View File

@ -1,4 +1,10 @@
namespace Admin.NET.Core.Ai.Option;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// LLM配置选项
@ -48,5 +54,4 @@ public class EmbeddingOptions
{
public string ModelId { get; set; }
public List<string> SupportModelIds { get; set; }
}
}

View File

@ -1,4 +1,10 @@
namespace Admin.NET.Core.Ai.Services.Chat.Dto;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
public class ChatInput
{
@ -6,14 +12,17 @@ public class ChatInput
/// 摘要id
/// </summary>
public long SummaryId { get; set; }
/// <summary>
/// 摘要
/// </summary>
public string? Summary { get; set; }
/// <summary>
/// 摘要唯一标识
/// </summary>
public string? UniqueToken { get; set; } = "";
/// <summary>
/// 消息
/// </summary>
@ -24,14 +33,13 @@ public class ChatInput
/// </summary>
public string ProviderName { get; set; }
/// <summary>
/// 模型id
/// </summary>
public string ModelId { get; set; }
/// <summary>
/// 深度思考
/// </summary>
public bool DeepThinking { get; set; } = false;
}
}

View File

@ -0,0 +1,11 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
public class ChatListInput : BasePageInput
{
}

View File

@ -0,0 +1,14 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// 聊天列表输出
/// </summary>
public class ChatListOutput : LLMChatSummaryHistory
{
}

View File

@ -0,0 +1,35 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
public class ChatOutput
{
/// <summary>
/// 唯一标识
/// </summary>
public string? UniqueToken { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Note { get; set; }
/// <summary>
/// 状态
/// </summary>
public bool State { get; set; } = true;
/// <summary>
/// 摘要
/// </summary>
public string? Summary { get; set; } = "";
/// <summary>
/// 摘要ID
/// </summary>
public long? SummaryId { get; set; }
}

View File

@ -0,0 +1,11 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
public class ModelListChangeInput : ModelListOutputItem
{
}

View File

@ -1,4 +1,10 @@
namespace Admin.NET.Core.Ai.Services.Chat.Dto;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// 模型列表输出
@ -33,11 +39,8 @@ public class ModelListOutputItem
/// </summary>
public string ProviderName { get; set; }
/// <summary>
/// 模型名称
/// </summary>
public string ModelName { get; set; }
}
}

View File

@ -1,17 +1,13 @@
using Admin.NET.Core.Ai.Enums;
using Admin.NET.Core.Ai.Option;
using Admin.NET.Core.Ai.Services.Chat.Dto;
using Admin.NET.Core.Ai.Services.DataBase;
using Admin.NET.Core.Ai.Services.DataBase.Dto;
using Microsoft.SemanticKernel.ChatCompletion;
using Admin.NET.Core.Ai.Services.SSE;
using LLMChatHistory = Admin.NET.Core.Ai.Entitys.LLMChatHistory;
using LLMChatSummaryHistory = Admin.NET.Core.Ai.Entitys.LLMChatSummaryHistory;
using Admin.NET.Core.Ai.Interface;
using Admin.NET.Core.Ai.Models;
using NewLife.Reflection;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Microsoft.SemanticKernel.ChatCompletion;
namespace Admin.NET.Core.Ai.Service;
namespace Admin.NET.Core.Ai.Services.Chat;
/// <summary>
/// 聊天补全核心服务
/// </summary>
@ -19,8 +15,10 @@ public class LLMChatCoreService : ITransient
{
private readonly ILogger<LLMChatCoreService> _logger;
private readonly IChatHistoryReducer _chatHistoryTruncationReducer; //截取器
// private readonly IChatHistoryReducer _chatHistorySummarizationReducer; //摘要器
private readonly ILLMFactory _llmFactory;
private readonly SseChannelManager _sseChannelManager; //给sse服务发送消息
private readonly SseDeepThinkingChannelManager _sseDeepThinkingChannelManager; //给sse服务发送深度思考消息
private readonly ChatChannelManager _dbAction; //操作数据库
@ -29,6 +27,7 @@ public class LLMChatCoreService : ITransient
private readonly UserManager _userManager;
private Kernel _kernel;
private readonly SysCacheService _sysCacheService;
public LLMChatCoreService(ILogger<LLMChatCoreService> logger,
ILLMFactory llmFactory,
SseChannelManager sseChannelManager,
@ -75,6 +74,7 @@ public class LLMChatCoreService : ITransient
}
#region
/// <summary>
/// 新聊天
/// </summary>
@ -93,6 +93,7 @@ public class LLMChatCoreService : ITransient
return await NormalNewChatCore(input, kernel, cancellationToken);
}
}
/// <summary>
/// 深度思考新聊天
/// </summary>
@ -103,7 +104,9 @@ public class LLMChatCoreService : ITransient
private async Task<ChatOutput> DeepThinkingNewChatCore(ChatInput input, Kernel kernel, CancellationToken cancellationToken)
{
ChatHistory chatHistory = [];
#region
var deepThinkingPrompt = $"""
AI助手,
```
@ -131,7 +134,9 @@ public class LLMChatCoreService : ITransient
await _sseDeepThinkingChannelManager.SendMessageAsync(_userManager.UserId, streamInput, cancellationToken);
}
await _sseDeepThinkingChannelManager.SendMessageAsync(_userManager.UserId, "[DONE]", cancellationToken);
#endregion
#endregion
//生成最终答案
return await NormalNewChatCore(input, kernel, cancellationToken, thinkingMessage);
}
@ -215,7 +220,7 @@ public class LLMChatCoreService : ITransient
};
DataActionInput dataActionInput = new DataActionInput
{
ActionType = ChatActionEnums.Append,
ActionType = ChatActionEnum.Append,
Item = chatSummaryHistory
};
await _dbAction.ActionWriter.WriteAsync(dataActionInput);
@ -227,7 +232,8 @@ public class LLMChatCoreService : ITransient
Summary = "New Chat"
};
}
#endregion
#endregion
#region
@ -251,6 +257,7 @@ public class LLMChatCoreService : ITransient
return await NormalContinueChatCoreAsync(input, kernel, uniqueToken, chatHistory, cancellationToken);
}
}
/// <summary>
/// 深度思考续聊
/// </summary>
@ -263,6 +270,7 @@ public class LLMChatCoreService : ITransient
private async Task<ChatOutput> DeepThinkingContinueChatCoreAsync(ChatInput input, Kernel kernel, string uniqueToken, ChatHistory chatHistory, CancellationToken cancellationToken)
{
#region
var deepThinkChatHistory = new ChatHistory(chatHistory);
var deepThinkingPrompt = $"""
AI助手,emoji表达人类情感****
@ -288,10 +296,12 @@ public class LLMChatCoreService : ITransient
}
await _sseDeepThinkingChannelManager.SendMessageAsync(_userManager.UserId, "[DONE]", cancellationToken);
#endregion
#endregion
//生成最终答案
return await NormalContinueChatCoreAsync(input, kernel, uniqueToken, chatHistory, cancellationToken, thinkingMessage);
}
/// <summary>
/// 普通续聊
/// </summary>
@ -329,7 +339,7 @@ public class LLMChatCoreService : ITransient
message += chunk.Content ?? "";
var streamInput = chunk.Content ?? "";
streamInput = streamInput.Replace("\n", "\\x0A");
//发送消息到SSE通道
//发送消息到SSE通道
await _sseChannelManager.SendMessageAsync(_userManager.UserId, streamInput, cancellationToken);
}
await _sseChannelManager.SendMessageAsync(_userManager.UserId, "[DONE]", cancellationToken);
@ -368,6 +378,7 @@ public class LLMChatCoreService : ITransient
}
input.SummaryId = chatSummaryHistory.Id;
}
private async Task _UpdateSummaryToDatabase(string summary, ChatInput input)
{
//更新摘要至数据库
@ -375,7 +386,7 @@ public class LLMChatCoreService : ITransient
{
await _dbAction.ActionWriter.WriteAsync(new DataActionInput
{
ActionType = ChatActionEnums.RenameSummary,
ActionType = ChatActionEnum.RenameSummary,
Item = new LLMChatSummaryHistory
{
Id = input.SummaryId,
@ -410,6 +421,7 @@ public class LLMChatCoreService : ITransient
);
return result.Content ?? "";
}
/// <summary>
/// 获取聊天历史,并截取
/// </summary>
@ -432,12 +444,15 @@ public class LLMChatCoreService : ITransient
case "user":
chatHistory.AddUserMessage(history.Content);
break;
case "assistant":
chatHistory.AddAssistantMessage(history.Content);
break;
case "system":
chatHistory.AddSystemMessage(history.Content);
break;
default:
break;
}
@ -453,6 +468,7 @@ public class LLMChatCoreService : ITransient
}
return chatHistory;
}
private async Task _SaveChatItemToDatabase(ChatHistory chatHistory, string uniqueToken)
{
var chatSummaryHistory = await _chatSummaryHistoryService.AsQueryable().Where(u => u.UniqueToken == uniqueToken).FirstAsync();
@ -474,14 +490,16 @@ public class LLMChatCoreService : ITransient
}
DataActionInput dataActionInput = new DataActionInput
{
ActionType = ChatActionEnums.AppendItem,
ActionType = ChatActionEnum.AppendItem,
Item = chatSummaryHistory
};
await _dbAction.ActionWriter.WriteAsync(dataActionInput);
}
#endregion
#endregion
#region
/// <summary>
/// 删除所属摘要的所有聊天记录
/// </summary>
@ -496,15 +514,17 @@ public class LLMChatCoreService : ITransient
}
DataActionInput dataActionInput = new DataActionInput
{
ActionType = ChatActionEnums.DeleteAll,
ActionType = ChatActionEnum.DeleteAll,
Item = chatSummaryHistory
};
await _dbAction.ActionWriter.WriteAsync(dataActionInput);
return true;
}
#endregion
#endregion
#region
/// <summary>
/// 重命名所属摘要的标签
/// </summary>
@ -520,12 +540,12 @@ public class LLMChatCoreService : ITransient
chatSummaryHistory.Summary = input.Summary;
DataActionInput dataActionInput = new DataActionInput
{
ActionType = ChatActionEnums.RenameSummary,
ActionType = ChatActionEnum.RenameSummary,
Item = chatSummaryHistory
};
await _dbAction.ActionWriter.WriteAsync(dataActionInput);
return true;
}
#endregion
}
#endregion
}

View File

@ -1,10 +1,10 @@
using Admin.NET.Core.Ai.Entitys;
using Admin.NET.Core.Ai.Option;
using Admin.NET.Core.Ai.Services.Chat.Dto;
using Admin.NET.Core.Ai.Services.DataBase;
using Admin.NET.Core.Ai.Services.SSE;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Services.Chat;
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// LLM聊天补全服务
@ -55,7 +55,7 @@ public class LLMChatService : IDynamicApiController, ITransient
[HttpPost]
[ApiDescriptionSettings(Description = "删除所属摘要的所有聊天记录", Name = "DeleteSummaryAll")]
public async Task<bool> DeleteSummaryAllAsync(ChatInput message) => await _chatCoreService.DeleteSummaryAllAsync(message);
/// <summary>
/// 重命名所属摘要的标签
/// </summary>
@ -95,7 +95,6 @@ public class LLMChatService : IDynamicApiController, ITransient
[ApiDescriptionSettings(Description = "获取模型列表", Name = "ModelList")]
public async Task<ModelListOutput> GetModelListAsync() => await _llmOptionService.GetModelListAsync();
/// <summary>
/// 切换模型
/// </summary>
@ -104,4 +103,4 @@ public class LLMChatService : IDynamicApiController, ITransient
/// <exception cref="Exception"></exception>
[ApiDescriptionSettings(Description = "切换模型", Name = "ChangeModel"), HttpPost]
public async Task<bool> ChangeModelAsync(ModelListChangeInput input) => await _llmOptionService.ChangeModelAsync(input);
}
}

View File

@ -1,7 +1,10 @@
using Admin.NET.Core.Ai.Option;
using Admin.NET.Core.Ai.Services.Chat.Dto;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Services.Chat;
namespace Admin.NET.Core.Ai.Service;
public class LLMOptionService : ITransient
{
@ -9,6 +12,7 @@ public class LLMOptionService : ITransient
private readonly UserManager _userManager;
private readonly IOptions<LLMOptions> _llmOptions;
private readonly SysCacheService _sysCacheService;
public LLMOptionService(ILogger<LLMOptionService> logger,
UserManager userManager,
IOptions<LLMOptions> llmOptions,
@ -19,6 +23,7 @@ public class LLMOptionService : ITransient
_llmOptions = llmOptions;
_sysCacheService = sysCacheService;
}
/// <summary>
/// 获取模型列表
/// 步骤:
@ -55,6 +60,7 @@ public class LLMOptionService : ITransient
_sysCacheService.Set(key, output);
return await Task.FromResult(output);
}
/// <summary>
/// 可能存在用户手动修改了配置文件,如删除、添加、修改模型,此时需要更新缓存中的模型列表
/// </summary>
@ -134,6 +140,7 @@ public class LLMOptionService : ITransient
}
return await _UpdateModelList(input, cache, key);
}
private async Task<bool> _SetNewModelList(ModelListChangeInput input, string key)
{
var currentModel = _llmOptions.Value.Providers.FirstOrDefault(it => it.ProductName == input.ProviderName);
@ -170,5 +177,4 @@ public class LLMOptionService : ITransient
_sysCacheService.Set(key, cache);
return await Task.FromResult(true);
}
}
}

View File

@ -1,8 +1,10 @@
namespace Admin.NET.Core.Ai.Services.DataBase;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core.Ai.Entitys;
using Admin.NET.Core.Ai.Enums;
using Admin.NET.Core.Ai.Services.DataBase.Dto;
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// 异步对数据库进行操作
@ -75,4 +77,4 @@ public class ChatChannelActionService : IScoped
.Updateable(oldChatSummaryHistory)
.ExecuteCommandAsync();
}
}
}

View File

@ -1,9 +1,12 @@
using System.Threading.Channels;
using Admin.NET.Core.Ai.Services.DataBase.Dto;
using Admin.NET.Core.Ai.Enums;
using Microsoft.Extensions.DependencyInjection;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Services.DataBase;
using System.Threading.Channels;
namespace Admin.NET.Core.Ai.Service;
public class ChatChannelManager : ISingleton
{
@ -35,16 +38,19 @@ public class ChatChannelManager : ISingleton
switch (action.ActionType)
{
case ChatActionEnums.Append:
case ChatActionEnum.Append:
await chatChannelActionService.AppendAsync(action.Item);
break;
case ChatActionEnums.AppendItem:
case ChatActionEnum.AppendItem:
await chatChannelActionService.AppendItemAsync(action.Item);
break;
case ChatActionEnums.DeleteAll:
case ChatActionEnum.DeleteAll:
await chatChannelActionService.DeleteAllAsync(action.Item);
break;
case ChatActionEnums.RenameSummary:
case ChatActionEnum.RenameSummary:
await chatChannelActionService.RenameSummaryAsync(action.Item);
break;
}
@ -59,4 +65,4 @@ public class ChatChannelManager : ISingleton
public ChannelReader<DataActionInput> ActionReader => _channel.Reader;
public ChannelWriter<DataActionInput> ActionWriter => _channel.Writer;
}
}

View File

@ -0,0 +1,13 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
public class DataActionInput
{
public ChatActionEnum ActionType { get; set; }
public LLMChatSummaryHistory Item { get; set; }
}

View File

@ -1,14 +1,15 @@
using System.Net;
using Admin.NET.Core.Ai.Handlers;
using Admin.NET.Core.Ai.Interface;
using Admin.NET.Core.Ai.Models;
using Admin.NET.Core.Ai.Option;
using Microsoft.SemanticKernel.Connectors.Ollama;
using OllamaSharp;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Amazon.BedrockRuntime;
using Amazon.Runtime;
using OllamaSharp;
using System.Net;
namespace Admin.NET.Core.Ai.Services.Infrastructure;
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// 模型切换工厂
@ -17,10 +18,12 @@ namespace Admin.NET.Core.Ai.Services.Infrastructure;
public class ChangeModelFactory : ILLMFactory, ITransient
{
private readonly IServiceProvider _serviceProvider;
public ChangeModelFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
/// <summary>
/// 创建Kernel实例
/// </summary>
@ -31,6 +34,7 @@ public class ChangeModelFactory : ILLMFactory, ITransient
var builder = Kernel.CreateBuilder();
return GetKernel(builder, modelInput);
}
/// <summary>
/// 获取Kernel实例
/// </summary>
@ -62,6 +66,7 @@ public class ChangeModelFactory : ILLMFactory, ITransient
}
ConfigureKernelCore(builder, httpClient, provider, modelInput.ModelId);
}
/// <summary>
/// 配置Kernel核心代码
/// </summary>
@ -85,8 +90,7 @@ public class ChangeModelFactory : ILLMFactory, ITransient
);
if (provider.ProductName == "OpenAI")
{
#pragma warning disable SKEXP0010 // 禁用预览API的警告
#pragma warning disable SKEXP0010 // 禁用预览API的警告
// 如果模型是OpenAI则添加音频转文字服务
builder.AddOpenAIAudioToText(
modelId: modelId,
@ -105,9 +109,10 @@ public class ChangeModelFactory : ILLMFactory, ITransient
apiKey: apiKey,
httpClient: httpClient
);
#pragma warning restore
#pragma warning restore
}
break;
case "AzureOpenAI":
builder.AddAzureOpenAIChatCompletion(
deploymentName: modelId,
@ -115,7 +120,7 @@ public class ChangeModelFactory : ILLMFactory, ITransient
apiKey: apiKey,
httpClient: httpClient
);
#pragma warning disable SKEXP0010 // 禁用预览API的警告
#pragma warning disable SKEXP0010 // 禁用预览API的警告
builder.AddAzureOpenAIAudioToText(
deploymentName: modelId,
endpoint: provider.ApiEndpoint,
@ -134,36 +139,40 @@ public class ChangeModelFactory : ILLMFactory, ITransient
apiKey: apiKey,
httpClient: httpClient
);
#pragma warning restore SKEXP0010 // 禁用预览API的警告
#pragma warning restore SKEXP0010 // 禁用预览API的警告
break;
case "Ollama":
var ollamaClient = new OllamaApiClient(
uriString: provider.ApiEndpoint,
defaultModel: modelId
);
#pragma warning disable SKEXP0070// 禁用预览API的警告
#pragma warning disable SKEXP0070// 禁用预览API的警告
builder.AddOllamaChatCompletion(
ollamaClient: ollamaClient
);
#pragma warning restore SKEXP0070// 禁用预览API的警告
#pragma warning restore SKEXP0070// 禁用预览API的警告
break;
case "Claude":
var credentials = new BasicAWSCredentials(provider.ApiKey, provider.ApiSecret);
var amazonBedrockRuntime = new AmazonBedrockRuntimeClient(credentials, new AmazonBedrockRuntimeConfig
{
RegionEndpoint = Amazon.RegionEndpoint.GetBySystemName(provider.Region)
});
#pragma warning disable SKEXP0070
#pragma warning disable SKEXP0070
builder.AddBedrockChatCompletionService(
modelId: modelId,
bedrockRuntime: amazonBedrockRuntime
);
#pragma warning restore SKEXP0070
#pragma warning restore SKEXP0070
break;
default:
throw new Exception($"未找到{provider.LLMType}模型提供者");
}
}
/// <summary>
/// 获取HttpClient实例,可能为null
/// </summary>
@ -203,5 +212,4 @@ public class ChangeModelFactory : ILLMFactory, ITransient
}
return httpMessageHandler == null ? null : new HttpClient(httpMessageHandler);
}
}
}

View File

@ -0,0 +1,128 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Newtonsoft.Json;
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// OpenRouter 客户端
/// </summary>
public class LLMOpenRouterClient
{
private readonly HttpClient _httpClient;
private readonly LLMCustomOptions _options;
public LLMOpenRouterClient(HttpClient httpClient, IOptions<LLMCustomOptions> options)
{
_httpClient = httpClient;
_options = options.Value;
_httpClient.Timeout = TimeSpan.FromSeconds(_options.Timeout);
}
/// <summary>
/// 获取提示词的LLM的回答
/// </summary>
/// <param name="messages">提示词</param>
/// <returns>提示词的LLM的回答</returns>
/// <exception cref="ArgumentException">消息列表不能为空</exception>
/// <exception cref="Exception">网络请求错误</exception>
/// <exception cref="JsonException">JSON解析错误</exception>
public async Task<string> GetPromptAsync(List<LLMInputMessage> messages)
{
return await GetLLMResponseAsync(messages, (messages) =>
{
if (!messages.Any(m => m.Role.Equals("system")))
{
messages.Insert(0, new LLMInputMessage()
{
Role = "system",
Content = _options.InitSystemPromptMessage
});
}
});
}
/// <summary>
/// 获取聊天记录的LLM的回答
/// </summary>
/// <param name="messages">聊天记录</param>
/// <returns>聊天记录的LLM的回答</returns>
/// <exception cref="ArgumentException">消息列表不能为空</exception>
/// <exception cref="Exception">网络请求错误</exception>
public async Task<string> GetChatAsync(List<LLMInputMessage> messages)
{
return await GetLLMResponseAsync(messages, (messages) =>
{
if (!messages.Any(m => m.Role.Equals("system")))
{
messages.Insert(0, new LLMInputMessage()
{
Role = "system",
Content = _options.InitSystemChatMessage
});
}
});
}
/// <summary>
/// 获取LLM的回答
/// </summary>
/// <param name="messages">消息列表</param>
/// <param name="beforeSendAction">在发送请求之前,可以对消息进行修改</param>
/// <returns>LLM的回答</returns>
/// <exception cref="ArgumentException">消息列表不能为空</exception>
/// <exception cref="Exception">网络请求错误</exception>
/// <exception cref="JsonException">JSON解析错误</exception>
private async Task<string> GetLLMResponseAsync(List<LLMInputMessage> messages, Action<List<LLMInputMessage>> beforeSendAction = null)
{
try
{
if (messages == null || !messages.Any())
throw new ArgumentException("Message list cannot be empty");
if (messages.Any(m => m.Content.Length > 2000))
throw new ArgumentException("Message content exceeds the maximum length limit");
var defaultLLM = _options.SupportLLMList.Find(item => item.Desciption.Equals(_options.ModelProvider));
if (defaultLLM == null)
{
throw new Exception("Default LLM not found, please check if the ModelProvider in ai.json is set incorrectly?");
}
var inputBody = new LLMInputBody();
inputBody.Model = defaultLLM.Model;
inputBody.Messages = messages;
var strBody = LLMJsonTool.SerializeObject(inputBody);
beforeSendAction?.Invoke(messages); // 在发送请求之前,可以对消息进行修改
using (var content = new StringContent(strBody, Encoding.UTF8, "application/json"))
using (var response = await _httpClient.PostAsync(_options.BaseUrl, content))
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var strResponse = await response.Content.ReadAsStringAsync();
var output = LLMJsonTool.DeserializeObject<LLMOutput>(strResponse);
return output.Choices[0].Message.Content;
}
else
{
throw new Exception("Failed to get LLM response: " + $"Status code: {response.StatusCode}" + " " + $"Error message: {response.ReasonPhrase}" + " " + $"Error content: {await response.Content.ReadAsStringAsync()}");
}
}
}
catch (HttpRequestException ex)
{
throw new Exception($"Network request error: {ex.Message}");
}
catch (JsonException ex)
{
throw new Exception($"JSON parsing error: {ex.Message}");
}
catch (Exception ex)
{
throw new Exception($"Unknown error: {ex.Message}");
}
}
}

View File

@ -1,7 +1,12 @@
using System.Threading.Channels;
using Admin.NET.Core.Ai.Interface;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Services.SSE;
using System.Threading.Channels;
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// SSE通道管理
@ -50,4 +55,4 @@ public class BaseSseChannelManager : ISseChannelManager
await channel.Writer.WriteAsync(message, cancellationToken);
}
}
}
}

View File

@ -0,0 +1,14 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// 聊天通道管理
/// </summary>
public class SseChannelManager : BaseSseChannelManager, ISingleton
{
}

View File

@ -0,0 +1,14 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// 深度思考通道管理
/// </summary>
public class SseDeepThinkingChannelManager : BaseSseChannelManager, ISingleton
{
}

View File

@ -1,4 +1,10 @@
namespace Admin.NET.Core.Ai.Services.SSE;
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Ai.Service;
/// <summary>
/// SSE服务
@ -71,5 +77,4 @@ public class SseService : ControllerBase
_sseDeepThinkingChannelManager.Unregister(id);
}
}
}
}

View File

@ -1,7 +0,0 @@
namespace Admin.NET.Core.Ai.Services.Chat.Dto;
public class ChatListInput : BasePageInput
{
}

View File

@ -1,10 +0,0 @@
using Admin.NET.Core.Ai.Entitys;
namespace Admin.NET.Core.Ai.Services.Chat.Dto;
/// <summary>
/// 聊天列表输出
/// </summary>
public class ChatListOutput:LLMChatSummaryHistory
{
}

View File

@ -1,30 +0,0 @@
namespace Admin.NET.Core.Ai.Services.Chat.Dto;
public class ChatOutput
{
/// <summary>
/// 唯一标识
/// </summary>
public string? UniqueToken { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Note { get; set; }
/// <summary>
/// 状态
/// </summary>
public bool State { get; set; } = true;
/// <summary>
/// 摘要
/// </summary>
public string? Summary { get; set; } = "";
/// <summary>
/// 摘要ID
/// </summary>
public long? SummaryId { get; set; }
}

View File

@ -1,7 +0,0 @@
namespace Admin.NET.Core.Ai.Services.Chat.Dto;
public class ModelListChangeInput:ModelListOutputItem
{
}

View File

@ -1,11 +0,0 @@
using Admin.NET.Core.Ai.Entitys;
using Admin.NET.Core.Ai.Enums;
namespace Admin.NET.Core.Ai.Services.DataBase.Dto;
public class DataActionInput
{
public ChatActionEnums ActionType { get; set; }
public LLMChatSummaryHistory Item { get; set; }
}

View File

@ -1,10 +0,0 @@
using System.Threading.Channels;
namespace Admin.NET.Core.Ai.Services.SSE;
/// <summary>
/// 聊天通道管理
/// </summary>
public class SseChannelManager: BaseSseChannelManager,ISingleton
{
}

View File

@ -1,10 +0,0 @@
using System.Threading.Channels;
namespace Admin.NET.Core.Ai.Services.SSE;
/// <summary>
/// 深度思考通道管理
/// </summary>
public class SseDeepThinkingChannelManager: BaseSseChannelManager,ISingleton
{
}

View File

@ -0,0 +1,51 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Admin.NET.Core;
/// <summary>
/// LLM JSON 工具类
/// </summary>
public class LLMJsonTool
{
/// <summary>
/// 序列化对象
/// </summary>
/// <param name="obj">对象</param>
/// <returns>JSON字符串</returns>
public static string SerializeObject(object obj)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};
return JsonConvert.SerializeObject(obj, settings);
}
/// <summary>
/// 反序列化JSON字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="json">JSON字符串</param>
/// <returns>对象</returns>
public static T DeserializeObject<T>(string json)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};
return JsonConvert.DeserializeObject<T>(json, settings);
}
}

View File

@ -34,6 +34,7 @@ global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options;
global using Microsoft.SemanticKernel;
global using NewLife;
global using NewLife.Caching;
global using Newtonsoft.Json.Linq;
@ -59,7 +60,4 @@ global using System.Text;
global using System.Text.RegularExpressions;
global using System.Web;
global using UAParser;
global using Yitter.IdGenerator;
global using Microsoft.SemanticKernel;
global using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
global using Yitter.IdGenerator;

View File

@ -157,13 +157,13 @@ public static class CodeGenHelper
{
return dataType.ToLower() switch
{
"tinytext" or "mediumtext" or "longtext" or "mid" or "text" or "varchar" or "char" or "nvarchar" or "nchar" or "timestamp" => "string",
"int" or "integer" => "int",
"tinytext" or "mediumtext" or "longtext" or "mid" or "text" or "varchar" or "char" or "nvarchar" or "nchar" or "string" or "timestamp" => "string",
"int" or "integer" or "int32" => "int",
"smallint" => "Int16",
//"tinyint" => "byte",
"tinyint" => "bool", // MYSQL
"bigint" => "long",
"bit" => "bool",
"tinyint" => "bool", // MYSQL
"bigint" or "int64" => "long",
"bit" or "boolean" => "bool",
"money" or "smallmoney" or "numeric" or "decimal" => "decimal",
"real" => "Single",
"datetime" or "datetime2" or "smalldatetime" => "DateTime",

View File

@ -5,7 +5,6 @@
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core;
using Admin.NET.Core.Ai.Option;
using AspNetCoreRateLimit;
using Furion;
using Microsoft.Extensions.DependencyInjection;
@ -44,8 +43,13 @@ public static class ProjectOptions
services.Configure<ClientRateLimitOptions>(App.Configuration.GetSection("ClientRateLimiting"));
services.Configure<ClientRateLimitPolicies>(App.Configuration.GetSection("ClientRateLimitPolicies"));
<<<<<<< HEAD
services.AddConfigurableOptions<LLMOptions>(); //基于Microsoft Semantic Kernel实现,也是本应用的默认实现
=======
services.AddConfigurableOptions<LLMCustomOptions>(); // 手动实现LLM接口配置选项
services.AddConfigurableOptions<LLMOptions>(); // 基于Microsoft Semantic Kernel实现,也是本应用的默认实现
>>>>>>> f408a01a2d83c2d384dcbdb16eab344e4d437ab3
return services;
}

View File

@ -5,7 +5,6 @@
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core;
using Admin.NET.Core.Ai.Extentions;
using Admin.NET.Core.Service;
using AspNetCoreRateLimit;
using Furion;
@ -137,10 +136,9 @@ public class Startup : AppStartup
// SqlSugar
services.AddSqlSugar();
#region LLM大模型
// 注册LLM大模型
services.AddLLMFactory(); // 注册LLM模型工厂,如果需要切换模型请引入ILLMFactory接口调用CreateKernel方法获取Kernel实例
services.AddLLM(); // 注册LLM大模型,注意:此扩展不能切换模型,只能使用一个模型
#endregion
// 三方授权登录OAuth
services.AddOAuth();
@ -421,12 +419,14 @@ public class Startup : AppStartup
options.EnableDirectoryBrowsing = false; // 是否启用目录浏览
options.Title = "定时任务看板"; // 自定义看板标题
options.LoginHandle = async (username, password, httpContext) =>
options.LoginConfig.OnLoging = async (username, password, httpContext) =>
{
var res = await httpContext.RequestServices.GetRequiredService<SysAuthService>().SwaggerSubmitUrl(new SpecificationAuth { UserName = username, Password = password });
return res == 200;
};
options.LoginSessionKey = "schedule_session_key"; // 登录客户端存储的 Session 键
options.LoginConfig.DefaultUsername = "";
options.LoginConfig.DefaultPassword = "";
options.LoginConfig.SessionKey = "schedule_session_key"; // 登录客户端存储的 Session 键
});
// 配置Swagger-Knife4UI路由前缀一致代表独立不同则代表共存

View File

@ -26,7 +26,7 @@
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
<PackageReference Include="Rezero.Api" Version="1.8.23" />
<PackageReference Include="Rezero.Api" Version="1.8.24" />
</ItemGroup>
<ItemGroup>

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro",
"type": "module",
"version": "2.4.33",
"lastBuildTime": "2025.06.17",
"lastBuildTime": "2025.06.19",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun",
"license": "MIT",
@ -70,7 +70,7 @@
"uuid": "^11.1.0",
"vcrontab-3": "^3.3.22",
"vform3-builds": "^3.0.10",
"vue": "^3.5.16",
"vue": "^3.5.17",
"vue-clipboard3": "^2.0.0",
"vue-demi": "0.14.10",
"vue-draggable-plus": "^0.6.0",
@ -83,15 +83,14 @@
"vue-router": "^4.5.1",
"vue-signature-pad": "^3.0.2",
"vue3-tree-org": "^4.2.2",
"vxe-pc-ui": "^4.6.24",
"vxe-table": "^4.13.40",
"vxe-pc-ui": "^4.6.26",
"vxe-table": "^4.13.42",
"xe-utils": "^3.7.5",
"xlsx-js-style": "^1.2.0"
},
"devDependencies": {
"@iconify/vue": "^5.0.0",
"@plugin-web-update-notification/vite": "^2.0.0",
"@types/franc": "^6.0.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.15.32",
"@types/nprogress": "^0.2.3",
@ -100,7 +99,7 @@
"@typescript-eslint/parser": "^8.34.1",
"@vitejs/plugin-vue": "^5.2.4",
"@vitejs/plugin-vue-jsx": "^4.2.0",
"@vue/compiler-sfc": "^3.5.16",
"@vue/compiler-sfc": "^3.5.17",
"code-inspector-plugin": "^0.20.12",
"eslint": "^9.29.0",
"eslint-plugin-vue": "^10.2.0",
@ -109,7 +108,7 @@
"prettier": "^3.5.3",
"rollup-plugin-visualizer": "^6.0.3",
"sass": "^1.89.2",
"terser": "^5.42.0",
"terser": "^5.43.0",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vite-plugin-cdn-import": "^1.0.1",

View File

@ -14,7 +14,7 @@
import { LLMChatSummaryHistory } from './llmchat-summary-history';
/**
*
*
*
* @export
* @interface LLMChatHistory

View File

@ -14,7 +14,7 @@
import { LLMChatHistory } from './llmchat-history';
/**
*
*
*
* @export
* @interface LLMChatSummaryHistory

View File

@ -261,7 +261,7 @@ const onLanguageChange = (lang: string) => {
//
const onChatClick = () => {
router.push({
path: '/llm/chat',
path: '/llm/aiChat',
});
};

View File

@ -26,13 +26,12 @@ import JwChat from 'jwchat';
import 'jwchat/lib/style.css';
// 自定义字典组件
import sysDict from '/@/components/sysDict/sysDict.vue';
// AI组件
import ElementPlusX from 'vue-element-plus-x';
import 'vue-markdown-shiki/style'
import markdownPlugin from 'vue-markdown-shiki' //markodwn渲染组件,由于element-plus-x的thinking不支持markdown所以需要单独引入,未来element-plus-x组成成熟应该会被移转
import markdownPlugin from 'vue-markdown-shiki'
// 关闭自动打印
import { disAutoConnect } from 'vue-plugin-hiprint';
disAutoConnect();
const app = createApp(App);

View File

@ -50,13 +50,13 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
children: [],
},
{
path: '/llm/chat',
name: 'chat',
component: () => import('/@/views/chat/index.vue'),
path: '/llm/aiChat',
name: 'aiChat',
component: () => import('/@/views/aiChat/index.vue'),
meta: {
title: 'AI对话',
isKeepAlive: true,
}
},
},
{
path: '/platform/job/dashboard',

View File

@ -1,3 +1,3 @@
declare module 'franc' {
export function franc(text: string): string;
}
export function franc(text: string): string;
}

View File

@ -1 +1 @@
declare module 'vue-element-plus-x';
declare module 'vue-element-plus-x';

View File

@ -1,19 +1,18 @@
import { useUserInfo } from '/@/stores/userInfo';
const {userInfos} = useUserInfo();
const { userInfos } = useUserInfo();
/**
*
* @returns
* @returns
*/
export const isSupperAdmin = (): boolean => {
return userInfos?.accountType === 999;
};
/**
*
* @returns
* @returns
*/
export const isTenantAdmin = (): boolean => {
return userInfos?.accountType === 888;
@ -21,7 +20,7 @@ export const isTenantAdmin = (): boolean => {
/**
*
* @returns
* @returns
*/
export const isAdmin = (): boolean => {
return isSupperAdmin() || isTenantAdmin();
@ -29,92 +28,91 @@ export const isAdmin = (): boolean => {
/**
*
* @returns
* @returns
*/
export const isNormalUser = (): boolean => {
return userInfos?.accountType === 777;
};
/**
*
* @returns
* @returns
*/
export const isMember = (): boolean => {
return userInfos?.accountType === 666;
};
};
/**
*
* @returns
*/
export const userEmail = ():string => {
export const userEmail = (): string => {
return userInfos?.email;
}
};
/**
* id
* @returns id
*/
export const userName = ():string => {
export const userName = (): string => {
return userInfos?.userName;
}
};
/**
*
* @returns
*/
export const userFriendName = ():string => (
userInfos?.realName ? userInfos?.realName : userInfos?.account ? userInfos?.account : userInfos?.email
)
export const userFriendName = (): string => (userInfos?.realName ? userInfos?.realName : userInfos?.account ? userInfos?.account : userInfos?.email);
/**
* id
* @returns id
*/
export const tenantId = ():number => {
export const tenantId = (): number => {
return userInfos?.tenantId;
}
};
/***
* .
*/
export const userAccount = ():string => userInfos?.account;
export const userAccount = (): string => userInfos?.account;
/**
*
* @returns
*/
export const userPhone = ():string => userInfos?.phone;
export const userPhone = (): string => userInfos?.phone;
/**
* id
* @returns id.
*/
export const userId = ():number => userInfos?.id;
export const userId = (): number => userInfos?.id;
/**
* id
* @returns id
*/
export const orgId = ():number => userInfos?.orgId;
export const orgId = (): number => userInfos?.orgId;
/**
*
* @returns
*/
export const orgName = ():string => userInfos?.orgName;
export const orgName = (): string => userInfos?.orgName;
/**
* id
* @returns id.
*/
export const posId = ():number => userInfos?.posId;
export const posId = (): number => userInfos?.posId;
/**
*
* @returns
*/
export const posName = ():string => userInfos?.posName;
export const posName = (): string => userInfos?.posName;
export const hasPrivilege = (privilege: string):boolean => {
export const hasPrivilege = (privilege: string): boolean => {
return userInfos.authApiList.includes(privilege);
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,10 @@ import { CodeInspectorPlugin } from 'code-inspector-plugin';
import fs from 'fs';
import { visualizer } from 'rollup-plugin-visualizer';
import { webUpdateNotice } from '@plugin-web-update-notification/vite';
import { copyPublicPlugin } from 'vite-plugin-forvmsc';
// monaco 菜单汉化 https://wf0.github.io/example/plugins/I18n.html
import monacoZhHans from './public/monaco/zh-hans.json';
import nlsPlugin, { Languages, esbuildPluginMonacoEditorNls } from './public/monaco/vite-plugin-i18n-nls';
import { copyPublicPlugin } from 'vite-plugin-forvmsc'
const pathResolve = (dir: string) => {
return resolve(__dirname, '.', dir);