mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-04-02 15:16:37 +08:00
Compare commits
14 Commits
url
...
4ab4d7b6db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ab4d7b6db | ||
|
|
d4d89b989c | ||
|
|
d463053c16 | ||
|
|
dbe5a95b47 | ||
|
|
c1c43c1464 | ||
|
|
13d6fc228a | ||
|
|
58ce45ec92 | ||
|
|
048a9b9601 | ||
|
|
097798268b | ||
|
|
f7eb1b7048 | ||
|
|
4133b80d49 | ||
|
|
57b03436f3 | ||
|
|
836ea90145 | ||
|
|
a040b7a16a |
@@ -40,6 +40,11 @@ public class TokenGetListOutputDto
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsDisabled { get; set; }
|
public bool IsDisabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用请求日志记录
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnableLog { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建时间
|
/// 创建时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ public class TokenService : ApplicationService
|
|||||||
PremiumQuotaLimit = t.PremiumQuotaLimit,
|
PremiumQuotaLimit = t.PremiumQuotaLimit,
|
||||||
PremiumUsedQuota = usedQuota,
|
PremiumUsedQuota = usedQuota,
|
||||||
IsDisabled = t.IsDisabled,
|
IsDisabled = t.IsDisabled,
|
||||||
|
IsEnableLog = t.IsEnableLog,
|
||||||
CreationTime = t.CreationTime
|
CreationTime = t.CreationTime
|
||||||
};
|
};
|
||||||
}).ToList();
|
}).ToList();
|
||||||
@@ -158,6 +159,7 @@ public class TokenService : ApplicationService
|
|||||||
PremiumQuotaLimit = token.PremiumQuotaLimit,
|
PremiumQuotaLimit = token.PremiumQuotaLimit,
|
||||||
PremiumUsedQuota = 0,
|
PremiumUsedQuota = 0,
|
||||||
IsDisabled = token.IsDisabled,
|
IsDisabled = token.IsDisabled,
|
||||||
|
IsEnableLog = token.IsEnableLog,
|
||||||
CreationTime = token.CreationTime
|
CreationTime = token.CreationTime
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
using Volo.Abp.Uow;
|
||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
using Yi.Framework.AiHub.Domain.Entities;
|
using Yi.Framework.AiHub.Domain.Entities;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
using Yi.Framework.AiHub.Domain.Extensions;
|
using Yi.Framework.AiHub.Domain.Extensions;
|
||||||
using Yi.Framework.AiHub.Domain.Managers;
|
using Yi.Framework.AiHub.Domain.Managers;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
@@ -33,10 +36,12 @@ public class OpenApiService : ApplicationService
|
|||||||
private readonly PremiumPackageManager _premiumPackageManager;
|
private readonly PremiumPackageManager _premiumPackageManager;
|
||||||
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreRepository;
|
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreRepository;
|
||||||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
||||||
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
||||||
ModelManager modelManager, AiBlacklistManager aiBlacklistManager,
|
ModelManager modelManager, AiBlacklistManager aiBlacklistManager,
|
||||||
IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreRepository, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreRepository, ISqlSugarRepository<AiModelEntity> aiModelRepository,
|
||||||
|
IServiceScopeFactory serviceScopeFactory)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -48,6 +53,7 @@ public class OpenApiService : ApplicationService
|
|||||||
_premiumPackageManager = premiumPackageManager;
|
_premiumPackageManager = premiumPackageManager;
|
||||||
_imageStoreRepository = imageStoreRepository;
|
_imageStoreRepository = imageStoreRepository;
|
||||||
_aiModelRepository = aiModelRepository;
|
_aiModelRepository = aiModelRepository;
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -91,6 +97,12 @@ public class OpenApiService : ApplicationService
|
|||||||
null, tokenId,
|
null, tokenId,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录请求日志
|
||||||
|
if (tokenValidation.IsEnableLog)
|
||||||
|
{
|
||||||
|
FireAndForgetMessageLog(JsonSerializer.Serialize(input), tokenValidation.Token, tokenValidation.TokenName, input.Model, ModelApiTypeEnum.Completions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -206,6 +218,12 @@ public class OpenApiService : ApplicationService
|
|||||||
null, tokenId,
|
null, tokenId,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录请求日志
|
||||||
|
if (tokenValidation.IsEnableLog)
|
||||||
|
{
|
||||||
|
FireAndForgetMessageLog(JsonSerializer.Serialize(input), tokenValidation.Token, tokenValidation.TokenName, input.Model, ModelApiTypeEnum.Messages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -258,6 +276,12 @@ public class OpenApiService : ApplicationService
|
|||||||
null, tokenId,
|
null, tokenId,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录请求日志
|
||||||
|
if (tokenValidation.IsEnableLog)
|
||||||
|
{
|
||||||
|
FireAndForgetMessageLog(JsonSerializer.Serialize(input), tokenValidation.Token, tokenValidation.TokenName, input.Model, ModelApiTypeEnum.Responses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -318,6 +342,12 @@ public class OpenApiService : ApplicationService
|
|||||||
null, tokenId,
|
null, tokenId,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录请求日志
|
||||||
|
if (tokenValidation.IsEnableLog)
|
||||||
|
{
|
||||||
|
FireAndForgetMessageLog(input.GetRawText(), tokenValidation.Token, tokenValidation.TokenName, modelId, ModelApiTypeEnum.GenerateContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 私有
|
#region 私有
|
||||||
@@ -357,5 +387,25 @@ public class OpenApiService : ApplicationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FireAndForgetMessageLog(string requestBody, string apiKey, string apiKeyName, string modelId, ModelApiTypeEnum apiType)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
|
||||||
|
var manager = scope.ServiceProvider.GetRequiredService<MessageLogManager>();
|
||||||
|
using var uow = uowManager.Begin(requiresNew: true);
|
||||||
|
await manager.CreateAsync(requestBody, apiKey, apiKeyName, modelId, apiType);
|
||||||
|
await uow.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "记录消息日志失败, 请求体长度: {RequestBodyLength}", requestBody?.Length ?? 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -99,8 +99,8 @@ public enum GoodsTypeEnum
|
|||||||
[Price(83.7, 3, 27.9)] [DisplayName("YiXinVip 3 month", "3个月", "短期体验")] [GoodsCategory(GoodsCategoryType.Vip)]
|
[Price(83.7, 3, 27.9)] [DisplayName("YiXinVip 3 month", "3个月", "短期体验")] [GoodsCategory(GoodsCategoryType.Vip)]
|
||||||
YiXinVip3 = 3,
|
YiXinVip3 = 3,
|
||||||
|
|
||||||
[Price(114.5, 5, 22.9)] [DisplayName("YiXinVip 5 month", "5个月", "年度热销")] [GoodsCategory(GoodsCategoryType.Vip)]
|
[Price(91.6, 4, 22.9)] [DisplayName("YiXinVip 4 month", "4个月", "年度热销")] [GoodsCategory(GoodsCategoryType.Vip)]
|
||||||
YiXinVip5 = 15,
|
YiXinVip5 = 14,
|
||||||
|
|
||||||
// 尊享包服务 - 需要VIP资格才能购买
|
// 尊享包服务 - 需要VIP资格才能购买
|
||||||
[Price(188.9, 0, 1750)]
|
[Price(188.9, 0, 1750)]
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using SqlSugar;
|
||||||
|
using Volo.Abp.Domain.Entities;
|
||||||
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
|
|
||||||
|
[SugarTable("Ai_Message_Log")]
|
||||||
|
public class MessageLogAggregateRoot : Entity<Guid>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 请求内容(httpbody)
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDataType = "text")]
|
||||||
|
public string? RequestBody { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求apikey
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(Length = 255)]
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求apikey名称
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(Length = 255)]
|
||||||
|
public string ApiKeyName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreationTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型id
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(Length = 64)]
|
||||||
|
public string ModelId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// api类型
|
||||||
|
/// </summary>
|
||||||
|
public ModelApiTypeEnum ApiType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// api类型名称
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(Length = 16)]
|
||||||
|
public string ApiTypeName { get; set; }
|
||||||
|
}
|
||||||
@@ -51,6 +51,11 @@ public class TokenAggregateRoot : FullAuditedAggregateRoot<Guid>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsDisabled { get; set; }
|
public bool IsDisabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用请求日志记录(仅数据库手动修改)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnableLog { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查Token是否可用
|
/// 检查Token是否可用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -642,7 +642,7 @@ public class AiGateWayManager : DomainService
|
|||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseResult.Item1=="exception")
|
if (responseResult.Item1.Contains("exception"))
|
||||||
{
|
{
|
||||||
//兼容部分ai工具问题
|
//兼容部分ai工具问题
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Volo.Abp.Domain.Services;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Domain.Managers;
|
||||||
|
|
||||||
|
public class MessageLogManager : DomainService
|
||||||
|
{
|
||||||
|
private readonly ISqlSugarRepository<MessageLogAggregateRoot> _repository;
|
||||||
|
|
||||||
|
public MessageLogManager(ISqlSugarRepository<MessageLogAggregateRoot> repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建消息日志
|
||||||
|
/// </summary>
|
||||||
|
public async Task CreateAsync(string requestBody, string apiKey, string apiKeyName, string modelId, ModelApiTypeEnum apiType)
|
||||||
|
{
|
||||||
|
var entity = new MessageLogAggregateRoot
|
||||||
|
{
|
||||||
|
RequestBody = requestBody,
|
||||||
|
ApiKey = apiKey,
|
||||||
|
ApiKeyName = apiKeyName,
|
||||||
|
ModelId = modelId,
|
||||||
|
ApiType = apiType,
|
||||||
|
ApiTypeName = apiType.ToString(),
|
||||||
|
CreationTime = DateTime.Now
|
||||||
|
};
|
||||||
|
await _repository.InsertAsync(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,16 @@ public class TokenValidationResult
|
|||||||
/// token
|
/// token
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token名称
|
||||||
|
/// </summary>
|
||||||
|
public string TokenName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用请求日志记录
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnableLog { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TokenManager : DomainService
|
public class TokenManager : DomainService
|
||||||
@@ -117,7 +127,9 @@ public class TokenManager : DomainService
|
|||||||
{
|
{
|
||||||
UserId = entity.UserId,
|
UserId = entity.UserId,
|
||||||
TokenId = entity.Id,
|
TokenId = entity.Id,
|
||||||
Token = entity.Token
|
Token = entity.Token,
|
||||||
|
TokenName = entity.Name,
|
||||||
|
IsEnableLog = entity.IsEnableLog
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Serilog.Events;
|
|||||||
using Yi.Abp.Web;
|
using Yi.Abp.Web;
|
||||||
|
|
||||||
//创建日志,可使用{SourceContext}记录
|
//创建日志,可使用{SourceContext}记录
|
||||||
|
var outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}【{SourceContext}】[{Level:u3}]{Message:lj}{NewLine}{Exception}";
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
//由于后端处理请求中,前端请求已经结束,此类日志可不记录
|
//由于后端处理请求中,前端请求已经结束,此类日志可不记录
|
||||||
.Filter.ByExcluding(log =>log.Exception?.GetType() == typeof(TaskCanceledException)||log.MessageTemplate.Text.Contains("\"message\": \"A task was canceled.\""))
|
.Filter.ByExcluding(log =>log.Exception?.GetType() == typeof(TaskCanceledException)||log.MessageTemplate.Text.Contains("\"message\": \"A task was canceled.\""))
|
||||||
@@ -11,10 +12,17 @@ Log.Logger = new LoggerConfiguration()
|
|||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Error)
|
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Error)
|
||||||
.MinimumLevel.Override("Quartz", LogEventLevel.Warning)
|
.MinimumLevel.Override("Quartz", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore.Cors.Infrastructure.CorsService", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore.Authorization.DefaultAuthorizationService", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore.Routing.EndpointMiddleware", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Hangfire.Server.ServerHeartbeatProcess", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Hangfire.Redis.StackExchange.FetchedJobsWatcher", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Hangfire.Processing.BackgroundExecution", LogEventLevel.Warning)
|
||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
.WriteTo.Async(c => c.File("logs/all/log-.txt", rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Debug))
|
.WriteTo.Async(c => c.File("logs/all/log-.txt", rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Debug,outputTemplate:outputTemplate))
|
||||||
.WriteTo.Async(c => c.File("logs/error/errorlog-.txt", rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Error))
|
.WriteTo.Async(c => c.File("logs/error/errorlog-.txt", rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Error,outputTemplate:outputTemplate))
|
||||||
.WriteTo.Async(c => c.Console())
|
.WriteTo.Async(c => c.Console(outputTemplate:outputTemplate))
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ namespace Yi.Abp.Web
|
|||||||
var app = context.GetApplicationBuilder();
|
var app = context.GetApplicationBuilder();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<RankingItemAggregateRoot>();
|
//app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<MessageLogAggregateRoot>();
|
||||||
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<ActivationCodeRecordAggregateRoot>();
|
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<ActivationCodeRecordAggregateRoot>();
|
||||||
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<UsageStatisticsAggregateRoot>();
|
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<UsageStatisticsAggregateRoot>();
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
"Bash(npm install marked --save)",
|
"Bash(npm install marked --save)",
|
||||||
"Bash(pnpm add marked)",
|
"Bash(pnpm add marked)",
|
||||||
"Bash(pnpm lint:*)",
|
"Bash(pnpm lint:*)",
|
||||||
"Bash(pnpm list:*)"
|
"Bash(pnpm list:*)",
|
||||||
|
"Bash(pnpm vue-tsc:*)",
|
||||||
|
"Bash(pnpm build:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -283,7 +283,7 @@
|
|||||||
appRendered = true;
|
appRendered = true;
|
||||||
checkAndHideLoader();
|
checkAndHideLoader();
|
||||||
}
|
}
|
||||||
}, 30000);
|
}, 60000);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ import { useDesignStore } from '@/stores';
|
|||||||
interface Props {
|
interface Props {
|
||||||
content: string;
|
content: string;
|
||||||
theme?: 'light' | 'dark' | 'auto';
|
theme?: 'light' | 'dark' | 'auto';
|
||||||
|
sanitize?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
content: '',
|
content: '',
|
||||||
theme: 'auto',
|
theme: 'auto',
|
||||||
|
sanitize: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const designStore = useDesignStore();
|
const designStore = useDesignStore();
|
||||||
@@ -94,7 +96,12 @@ const renderer = {
|
|||||||
|
|
||||||
// 行内代码
|
// 行内代码
|
||||||
codespan(token: { text: string }) {
|
codespan(token: { text: string }) {
|
||||||
return `<code class="inline-code">${token.text}</code>`;
|
// 转义 HTML 标签,防止 <script> 等标签被浏览器解析
|
||||||
|
const escapedText = token.text
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
return `<code class="inline-code">${escapedText}</code>`;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 链接
|
// 链接
|
||||||
@@ -148,11 +155,23 @@ async function renderContent(content: string) {
|
|||||||
// 包装表格,添加 table-wrapper 以支持横向滚动
|
// 包装表格,添加 table-wrapper 以支持横向滚动
|
||||||
rawHtml = rawHtml.replace(/<table>/g, '<div class="table-wrapper"><table>');
|
rawHtml = rawHtml.replace(/<table>/g, '<div class="table-wrapper"><table>');
|
||||||
rawHtml = rawHtml.replace(/<\/table>/g, '</table></div>');
|
rawHtml = rawHtml.replace(/<\/table>/g, '</table></div>');
|
||||||
// 使用 DOMPurify 清理 HTML,防止 XSS
|
// 转义 script 标签,防止浏览器将其当作真实脚本解析
|
||||||
renderedHtml.value = DOMPurify.sanitize(rawHtml, {
|
// 使用字符串拼接避免在源码中出现 script 标签字面量
|
||||||
ADD_TAGS: ['iframe'],
|
const scriptStart = '<' + 'script';
|
||||||
ADD_ATTR: ['target', 'data-code', 'data-html'],
|
const scriptEnd = '<' + '/script' + '>';
|
||||||
});
|
rawHtml = rawHtml.replace(new RegExp(scriptStart + '(.*?)>', 'gi'), '<script$1>');
|
||||||
|
rawHtml = rawHtml.replace(new RegExp(scriptEnd.replace('/', '\\/'), 'gi'), '</script>');
|
||||||
|
|
||||||
|
// 使用 DOMPurify 清理 HTML,防止 XSS(可通过 sanitize 属性禁用)
|
||||||
|
if (props.sanitize) {
|
||||||
|
renderedHtml.value = DOMPurify.sanitize(rawHtml, {
|
||||||
|
ADD_TAGS: ['iframe'],
|
||||||
|
ADD_ATTR: ['target', 'data-code', 'data-html'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderedHtml.value = rawHtml;
|
||||||
|
}
|
||||||
|
|
||||||
// 渲染后绑定按钮事件
|
// 渲染后绑定按钮事件
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ interface TokenFormData {
|
|||||||
expireTime: string;
|
expireTime: string;
|
||||||
premiumQuotaLimit: number | null;
|
premiumQuotaLimit: number | null;
|
||||||
quotaUnit: string;
|
quotaUnit: string;
|
||||||
|
isEnableLog?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -42,6 +43,7 @@ const localFormData = ref<TokenFormData>({
|
|||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
const neverExpire = ref(false); // 永不过期开关
|
const neverExpire = ref(false); // 永不过期开关
|
||||||
const unlimitedQuota = ref(false); // 无限制额度开关
|
const unlimitedQuota = ref(false); // 无限制额度开关
|
||||||
|
const isEnableLog = ref(false); // 是否启用请求日志(只读)
|
||||||
|
|
||||||
// 移动端检测
|
// 移动端检测
|
||||||
const isMobile = ref(false);
|
const isMobile = ref(false);
|
||||||
@@ -107,6 +109,9 @@ watch(() => props.visible, (newVal) => {
|
|||||||
// 判断是否永不过期
|
// 判断是否永不过期
|
||||||
neverExpire.value = !props.formData.expireTime;
|
neverExpire.value = !props.formData.expireTime;
|
||||||
|
|
||||||
|
// 读取是否启用请求日志(只读字段)
|
||||||
|
isEnableLog.value = props.formData.isEnableLog || false;
|
||||||
|
|
||||||
localFormData.value = {
|
localFormData.value = {
|
||||||
...props.formData,
|
...props.formData,
|
||||||
premiumQuotaLimit: displayValue,
|
premiumQuotaLimit: displayValue,
|
||||||
@@ -196,13 +201,13 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 API密钥'
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
:model-value="visible"
|
:model-value="visible"
|
||||||
:title="dialogTitle"
|
:title="dialogTitle"
|
||||||
:width="isMobile ? '95%' : '540px'"
|
:width="isMobile ? '95%' : '640px'"
|
||||||
:fullscreen="isMobile"
|
:fullscreen="isMobile"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:show-close="!submitting"
|
:show-close="!submitting"
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
>
|
>
|
||||||
<el-form :model="localFormData" :label-width="isMobile ? '100%' : '110px'" :label-position="isMobile ? 'top' : 'right'">
|
<el-form :model="localFormData" :label-width="isMobile ? '100%' : '150px'" :label-position="isMobile ? 'top' : 'right'">
|
||||||
<el-form-item label="API密钥名称" required>
|
<el-form-item label="API密钥名称" required>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="localFormData.name"
|
v-model="localFormData.name"
|
||||||
@@ -288,6 +293,21 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 API密钥'
|
|||||||
超出配额后API密钥将无法继续使用
|
超出配额后API密钥将无法继续使用
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 仅编辑模式显示:请求日志开关(只读) -->
|
||||||
|
<el-form-item v-if="mode === 'edit'" label="请求日志存储">
|
||||||
|
<div class="form-item-inline">
|
||||||
|
<el-switch
|
||||||
|
v-model="isEnableLog"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<span class="switch-status-text">{{ isEnableLog ? '已开启' : '已关闭' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-hint warning-hint">
|
||||||
|
<el-icon><i-ep-warning-filled /></el-icon>
|
||||||
|
此临时存储功能仅面向企业套餐用户,仅用于企业内部审计
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -356,6 +376,15 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 API密钥'
|
|||||||
color: #409eff;
|
color: #409eff;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.warning-hint {
|
||||||
|
background: #fdf6ec;
|
||||||
|
border-left-color: #e6a23c;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
@@ -364,6 +393,18 @@ const dialogTitle = computed(() => props.mode === 'create' ? '新增 API密钥'
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch-status-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item-inline {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-form-item__label) {
|
:deep(.el-form-item__label) {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const vipRemainingDays = computed(() => {
|
|||||||
// VIP到期状态文本
|
// VIP到期状态文本
|
||||||
const vipExpireStatusText = computed(() => {
|
const vipExpireStatusText = computed(() => {
|
||||||
if (!userVipStatus.value) return '';
|
if (!userVipStatus.value) return '';
|
||||||
if (!vipExpireTime.value) return '永久VIP';
|
if (!vipExpireTime.value) return '-';
|
||||||
if (vipRemainingDays.value === null) return '';
|
if (vipRemainingDays.value === null) return '';
|
||||||
if (vipRemainingDays.value < 0) return '已过期';
|
if (vipRemainingDays.value < 0) return '已过期';
|
||||||
if (vipRemainingDays.value === 0) return '今日到期';
|
if (vipRemainingDays.value === 0) return '今日到期';
|
||||||
@@ -202,7 +202,7 @@ function bindWechat() {
|
|||||||
{{ formatDate(vipExpireTime)?.split(' ')[0] || '-' }}
|
{{ formatDate(vipExpireTime)?.split(' ')[0] || '-' }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
永久
|
-
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-label">
|
<div class="stat-label">
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useHookFetch } from 'hook-fetch/vue';
|
|
||||||
import { ElMessage } from 'element-plus';
|
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
import type { AnyObject } from 'typescript-api-pro';
|
import type { AnyObject } from 'typescript-api-pro';
|
||||||
import { deleteMessages, unifiedSend } from '@/api';
|
|
||||||
import { useModelStore } from '@/stores/modules/model';
|
|
||||||
import { convertToApiFormat, parseStreamChunk, type UnifiedMessage } from '@/utils/apiFormatConverter';
|
|
||||||
import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
|
import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
|
||||||
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
|
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
|
||||||
|
import type { UnifiedMessage } from '@/utils/apiFormatConverter';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { useHookFetch } from 'hook-fetch/vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { unifiedSend } from '@/api';
|
||||||
|
import { useModelStore } from '@/stores/modules/model';
|
||||||
|
import { convertToApiFormat, parseStreamChunk } from '@/utils/apiFormatConverter';
|
||||||
|
|
||||||
export type MessageRole = 'ai' | 'user' | 'assistant' | string;
|
export type MessageRole = 'ai' | 'user' | 'assistant' | string;
|
||||||
|
|
||||||
@@ -83,7 +84,8 @@ export function useChatSender(options: UseChatSenderOptions) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const latest = messages[messages.length - 1];
|
const latest = messages[messages.length - 1];
|
||||||
if (!latest) return;
|
if (!latest)
|
||||||
|
return;
|
||||||
|
|
||||||
// 处理 token 使用情况
|
// 处理 token 使用情况
|
||||||
if (parsed.usage) {
|
if (parsed.usage) {
|
||||||
@@ -99,7 +101,8 @@ export function useChatSender(options: UseChatSenderOptions) {
|
|||||||
latest.thinkingStatus = 'thinking';
|
latest.thinkingStatus = 'thinking';
|
||||||
latest.loading = true;
|
latest.loading = true;
|
||||||
latest.thinlCollapse = true;
|
latest.thinlCollapse = true;
|
||||||
if (!latest.reasoning_content) latest.reasoning_content = '';
|
if (!latest.reasoning_content)
|
||||||
|
latest.reasoning_content = '';
|
||||||
latest.reasoning_content += parsed.reasoning_content;
|
latest.reasoning_content += parsed.reasoning_content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,21 +111,26 @@ export function useChatSender(options: UseChatSenderOptions) {
|
|||||||
const thinkStart = parsed.content.includes('<think>');
|
const thinkStart = parsed.content.includes('<think>');
|
||||||
const thinkEnd = parsed.content.includes('</think>');
|
const thinkEnd = parsed.content.includes('</think>');
|
||||||
|
|
||||||
if (thinkStart) isThinking.value = true;
|
if (thinkStart)
|
||||||
if (thinkEnd) isThinking.value = false;
|
isThinking.value = true;
|
||||||
|
if (thinkEnd)
|
||||||
|
isThinking.value = false;
|
||||||
|
|
||||||
if (isThinking.value) {
|
if (isThinking.value) {
|
||||||
latest.thinkingStatus = 'thinking';
|
latest.thinkingStatus = 'thinking';
|
||||||
latest.loading = true;
|
latest.loading = true;
|
||||||
latest.thinlCollapse = true;
|
latest.thinlCollapse = true;
|
||||||
if (!latest.reasoning_content) latest.reasoning_content = '';
|
if (!latest.reasoning_content)
|
||||||
|
latest.reasoning_content = '';
|
||||||
latest.reasoning_content += parsed.content
|
latest.reasoning_content += parsed.content
|
||||||
.replace('<think>', '')
|
.replace('<think>', '')
|
||||||
.replace('</think>', '');
|
.replace('</think>', '');
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
latest.thinkingStatus = 'end';
|
latest.thinkingStatus = 'end';
|
||||||
latest.loading = false;
|
latest.loading = false;
|
||||||
if (!latest.content) latest.content = '';
|
if (!latest.content)
|
||||||
|
latest.content = '';
|
||||||
latest.content += parsed.content;
|
latest.content += parsed.content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +152,8 @@ export function useChatSender(options: UseChatSenderOptions) {
|
|||||||
textFiles: any[],
|
textFiles: any[],
|
||||||
onUpdate: (messages: MessageItem[]) => void,
|
onUpdate: (messages: MessageItem[]) => void,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (isSending.value) return;
|
if (isSending.value)
|
||||||
|
return;
|
||||||
|
|
||||||
isSending.value = true;
|
isSending.value = true;
|
||||||
currentRequestApiType.value = modelStore.currentModelInfo.modelApiType || 'Completions';
|
currentRequestApiType.value = modelStore.currentModelInfo.modelApiType || 'Completions';
|
||||||
@@ -207,7 +216,8 @@ export function useChatSender(options: UseChatSenderOptions) {
|
|||||||
fileContent += `<FILE_NAME>${fileItem.name}</FILE_NAME>\n`;
|
fileContent += `<FILE_NAME>${fileItem.name}</FILE_NAME>\n`;
|
||||||
fileContent += `<FILE_CONTENT>\n${fileItem.fileContent}\n</FILE_CONTENT>\n`;
|
fileContent += `<FILE_CONTENT>\n${fileItem.fileContent}\n</FILE_CONTENT>\n`;
|
||||||
fileContent += `</ATTACHMENT_FILE>\n`;
|
fileContent += `</ATTACHMENT_FILE>\n`;
|
||||||
if (index < textFiles.length - 1) fileContent += '\n';
|
if (index < textFiles.length - 1)
|
||||||
|
fileContent += '\n';
|
||||||
});
|
});
|
||||||
contentArray.push({ type: 'text', text: fileContent });
|
contentArray.push({ type: 'text', text: fileContent });
|
||||||
}
|
}
|
||||||
@@ -225,7 +235,8 @@ export function useChatSender(options: UseChatSenderOptions) {
|
|||||||
baseMessage.content = contentArray.length > 1 || imageFiles.length > 0 || textFiles.length > 0
|
baseMessage.content = contentArray.length > 1 || imageFiles.length > 0 || textFiles.length > 0
|
||||||
? contentArray
|
? contentArray
|
||||||
: item.content;
|
: item.content;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
baseMessage.content = (item.role === 'ai' || item.role === 'assistant') && item.content.length > 10000
|
baseMessage.content = (item.role === 'ai' || item.role === 'assistant') && item.content.length > 10000
|
||||||
? `${item.content.substring(0, 10000)}...(内容过长,已省略)`
|
? `${item.content.substring(0, 10000)}...(内容过长,已省略)`
|
||||||
: item.content;
|
: item.content;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const apiList = [
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stream": true,
|
"stream": true,
|
||||||
"model": "gpt-5.3-codex"
|
"model": "gpt-5.2-chat"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -148,7 +148,7 @@ async function copyText(text: string) {
|
|||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="leading-normal text-sm">
|
<div class="leading-normal text-sm">
|
||||||
自 2025 年末起,AI 领域接口标准逐渐分化,原有的统一接口 <code class="bg-yellow-100 dark:bg-yellow-900 px-1 rounded">/v1/chat/completions</code> 已不再兼容所有模型。各厂商推出的新接口差异较大,接入第三方工具时,请务必根据具体模型选择正确的 API 类型。您可前往
|
自 2025 年末起,AI 领域接口标准逐渐分化,原有的统一接口 <code class="bg-yellow-100 px-1 rounded">/v1/chat/completions</code> 已不再兼容所有模型。各厂商推出的新接口差异较大,接入第三方工具时,请务必根据具体模型选择正确的 API 类型。您可前往
|
||||||
<router-link to="/model-library" class="text-primary font-bold hover:underline">模型库</router-link>
|
<router-link to="/model-library" class="text-primary font-bold hover:underline">模型库</router-link>
|
||||||
查看各模型对应的 API 信息。
|
查看各模型对应的 API 信息。
|
||||||
</div>
|
</div>
|
||||||
@@ -203,7 +203,7 @@ async function copyText(text: string) {
|
|||||||
<span class="font-bold text-sm">调用示例 (cURL)</span>
|
<span class="font-bold text-sm">调用示例 (cURL)</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="code-block bg-gray-50 dark:bg-[#161b22] p-3 rounded-md border border-gray-200 dark:border-gray-700">
|
<div class="code-block bg-gray-50 p-3 rounded-md border border-gray-200 ">
|
||||||
<pre class="text-xs overflow-x-auto font-mono m-0"><code class="language-bash">curl {{ fullUrl }} \
|
<pre class="text-xs overflow-x-auto font-mono m-0"><code class="language-bash">curl {{ fullUrl }} \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||||
|
|||||||
@@ -127,17 +127,20 @@ export function toResponsesFormat(messages: UnifiedMessage[]): ResponsesMessage[
|
|||||||
* 将统一格式的消息转换为 Anthropic Claude 格式
|
* 将统一格式的消息转换为 Anthropic Claude 格式
|
||||||
*/
|
*/
|
||||||
export function toClaudeFormat(messages: UnifiedMessage[]): { messages: ClaudeMessage[]; system?: string } {
|
export function toClaudeFormat(messages: UnifiedMessage[]): { messages: ClaudeMessage[]; system?: string } {
|
||||||
let systemPrompt: string | undefined;
|
|
||||||
const claudeMessages: ClaudeMessage[] = [];
|
const claudeMessages: ClaudeMessage[] = [];
|
||||||
|
|
||||||
for (const msg of messages) {
|
for (const msg of messages) {
|
||||||
// Claude 的 system 消息需要单独提取
|
// system 消息转换为 assistant 角色放入 messages 数组
|
||||||
|
let role: 'user' | 'assistant';
|
||||||
if (msg.role === 'system') {
|
if (msg.role === 'system') {
|
||||||
systemPrompt = typeof msg.content === 'string' ? msg.content : msg.content.map(c => c.text || '').join('');
|
role = 'assistant';
|
||||||
continue;
|
}
|
||||||
|
else if (msg.role === 'model') {
|
||||||
|
role = 'assistant';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
role = msg.role as 'user' | 'assistant';
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = msg.role === 'model' ? 'assistant' : msg.role;
|
|
||||||
|
|
||||||
// 处理内容格式
|
// 处理内容格式
|
||||||
let content: string | ClaudeContent[];
|
let content: string | ClaudeContent[];
|
||||||
@@ -181,7 +184,7 @@ export function toClaudeFormat(messages: UnifiedMessage[]): { messages: ClaudeMe
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { messages: claudeMessages, system: systemPrompt };
|
return { messages: claudeMessages };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -518,16 +521,16 @@ export function convertToApiFormat(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case ApiFormatType.Messages: {
|
case ApiFormatType.Messages: {
|
||||||
const { messages: claudeMessages, system } = toClaudeFormat(messages);
|
const { messages: claudeMessages } = toClaudeFormat(messages);
|
||||||
const request: any = {
|
const request: any = {
|
||||||
model,
|
model,
|
||||||
messages: claudeMessages,
|
messages: claudeMessages,
|
||||||
max_tokens: 32000,
|
max_tokens: 32000,
|
||||||
stream,
|
stream,
|
||||||
};
|
};
|
||||||
if (system) {
|
// if (system) {
|
||||||
request.system = system;
|
// request.system = system;
|
||||||
}
|
// }
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
case ApiFormatType.GenerateContent: {
|
case ApiFormatType.GenerateContent: {
|
||||||
|
|||||||
1
Yi.Ai.Vue3/types/components.d.ts
vendored
1
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -54,7 +54,6 @@ declare module 'vue' {
|
|||||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
ElRow: typeof import('element-plus/es')['ElRow']
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||||
ElSegmented: typeof import('element-plus/es')['ElSegmented']
|
|
||||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||||
|
|||||||
Reference in New Issue
Block a user