feat: 准备构建图片生成
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
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.Model;
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
using Yi.Framework.AiHub.Domain.Extensions;
|
using Yi.Framework.AiHub.Domain.Extensions;
|
||||||
using Yi.Framework.AiHub.Domain.Managers;
|
using Yi.Framework.AiHub.Domain.Managers;
|
||||||
@@ -30,11 +31,11 @@ public class OpenApiService : ApplicationService
|
|||||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly PremiumPackageManager _premiumPackageManager;
|
private readonly PremiumPackageManager _premiumPackageManager;
|
||||||
|
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreRepository;
|
||||||
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
||||||
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
||||||
ISqlSugarRepository<AiModelEntity> aiModelRepository, AiBlacklistManager aiBlacklistManager,
|
ISqlSugarRepository<AiModelEntity> aiModelRepository, AiBlacklistManager aiBlacklistManager,
|
||||||
IAccountService accountService, PremiumPackageManager premiumPackageManager)
|
IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreRepository)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -44,6 +45,7 @@ public class OpenApiService : ApplicationService
|
|||||||
_aiBlacklistManager = aiBlacklistManager;
|
_aiBlacklistManager = aiBlacklistManager;
|
||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_premiumPackageManager = premiumPackageManager;
|
_premiumPackageManager = premiumPackageManager;
|
||||||
|
_imageStoreRepository = imageStoreRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -259,11 +261,13 @@ public class OpenApiService : ApplicationService
|
|||||||
/// 生成-Gemini (尊享服务专用)
|
/// 生成-Gemini (尊享服务专用)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input"></param>
|
/// <param name="input"></param>
|
||||||
|
/// <param name="isAsync"></param>
|
||||||
/// <param name="modelId"></param>
|
/// <param name="modelId"></param>
|
||||||
/// <param name="alt"></param>
|
/// <param name="alt"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
[HttpPost("openApi/v1beta/models/{modelId}:{action:regex(^(generateContent|streamGenerateContent)$)}")]
|
[HttpPost("openApi/v1beta/models/{modelId}:{action:regex(^(generateContent|streamGenerateContent)$)}")]
|
||||||
public async Task GenerateContentAsync([FromBody] JsonElement input,
|
public async Task GenerateContentAsync([FromBody] JsonElement input,
|
||||||
|
[FromQuery] bool isAsync,
|
||||||
[FromRoute] string modelId,
|
[FromRoute] string modelId,
|
||||||
[FromQuery] string? alt, CancellationToken cancellationToken)
|
[FromQuery] string? alt, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -294,6 +298,18 @@ public class OpenApiService : ApplicationService
|
|||||||
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//如果异步,直接走job处理进行存储
|
||||||
|
if (isAsync)
|
||||||
|
{
|
||||||
|
var task = new ImageStoreTaskAggregateRoot();
|
||||||
|
await _imageStoreRepository.InsertAsync(task);
|
||||||
|
await _httpContextAccessor.HttpContext.Response.WriteAsJsonAsync(new
|
||||||
|
{
|
||||||
|
Id = task.Id
|
||||||
|
}, cancellationToken);
|
||||||
|
//todo 发送job,参数怎么办?需要先全存下来吗?全存下来,就要解析全部提示词 和 附件内容了
|
||||||
|
|
||||||
|
}
|
||||||
//ai网关代理httpcontext
|
//ai网关代理httpcontext
|
||||||
if (alt == "sse")
|
if (alt == "sse")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,4 +30,16 @@ public static class GeminiGenerateContentAcquirer
|
|||||||
TotalTokens = inputTokens + outputTokens,
|
TotalTokens = inputTokens + outputTokens,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取图片url,不包含前缀
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetImageBase64(JsonElement response)
|
||||||
|
{
|
||||||
|
//todo
|
||||||
|
//获取他的base64字符串
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
|
||||||
|
public enum TaskStatusEnum
|
||||||
|
{
|
||||||
|
Processing,
|
||||||
|
Success,
|
||||||
|
Fail
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using SqlSugar;
|
||||||
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||||
|
|
||||||
|
[SugarTable("Ai_ImageStoreTask")]
|
||||||
|
public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot<Guid>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 图片绝对路径
|
||||||
|
/// </summary>
|
||||||
|
public string? StoreUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务状态
|
||||||
|
/// </summary>
|
||||||
|
public TaskStatusEnum TaskStatus { get; set; } = TaskStatusEnum.Processing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户id
|
||||||
|
/// </summary>
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置成功
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storeUrl"></param>
|
||||||
|
public void SetSuccess(string storeUrl)
|
||||||
|
{
|
||||||
|
TaskStatus = TaskStatusEnum.Success;
|
||||||
|
StoreUrl = storeUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ using Newtonsoft.Json.Serialization;
|
|||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
using Yi.Framework.AiHub.Domain.AiGateWay;
|
using Yi.Framework.AiHub.Domain.AiGateWay;
|
||||||
using Yi.Framework.AiHub.Domain.AiGateWay.Exceptions;
|
using Yi.Framework.AiHub.Domain.AiGateWay.Exceptions;
|
||||||
|
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.Shared.Consts;
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Dtos;
|
using Yi.Framework.AiHub.Domain.Shared.Dtos;
|
||||||
@@ -39,11 +40,12 @@ public class AiGateWayManager : DomainService
|
|||||||
private readonly UsageStatisticsManager _usageStatisticsManager;
|
private readonly UsageStatisticsManager _usageStatisticsManager;
|
||||||
private readonly ISpecialCompatible _specialCompatible;
|
private readonly ISpecialCompatible _specialCompatible;
|
||||||
private PremiumPackageManager? _premiumPackageManager;
|
private PremiumPackageManager? _premiumPackageManager;
|
||||||
|
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreTaskRepository;
|
||||||
|
|
||||||
public AiGateWayManager(ISqlSugarRepository<AiAppAggregateRoot> aiAppRepository, ILogger<AiGateWayManager> logger,
|
public AiGateWayManager(ISqlSugarRepository<AiAppAggregateRoot> aiAppRepository, ILogger<AiGateWayManager> logger,
|
||||||
AiMessageManager aiMessageManager, UsageStatisticsManager usageStatisticsManager,
|
AiMessageManager aiMessageManager, UsageStatisticsManager usageStatisticsManager,
|
||||||
ISpecialCompatible specialCompatible, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
ISpecialCompatible specialCompatible, ISqlSugarRepository<AiModelEntity> aiModelRepository,
|
||||||
|
ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreTaskRepository)
|
||||||
{
|
{
|
||||||
_aiAppRepository = aiAppRepository;
|
_aiAppRepository = aiAppRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -51,6 +53,7 @@ public class AiGateWayManager : DomainService
|
|||||||
_usageStatisticsManager = usageStatisticsManager;
|
_usageStatisticsManager = usageStatisticsManager;
|
||||||
_specialCompatible = specialCompatible;
|
_specialCompatible = specialCompatible;
|
||||||
_aiModelRepository = aiModelRepository;
|
_aiModelRepository = aiModelRepository;
|
||||||
|
_imageStoreTaskRepository = imageStoreTaskRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PremiumPackageManager PremiumPackageManager =>
|
private PremiumPackageManager PremiumPackageManager =>
|
||||||
@@ -93,7 +96,7 @@ public class AiGateWayManager : DomainService
|
|||||||
return aiModelDescribe;
|
return aiModelDescribe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 聊天完成-非流式
|
/// 聊天完成-非流式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -155,7 +158,7 @@ public class AiGateWayManager : DomainService
|
|||||||
await response.WriteAsJsonAsync(data, cancellationToken);
|
await response.WriteAsJsonAsync(data, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 聊天完成-缓存处理
|
/// 聊天完成-缓存处理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -185,8 +188,8 @@ public class AiGateWayManager : DomainService
|
|||||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.OpenAi, request.Model);
|
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.OpenAi, request.Model);
|
||||||
var chatService =
|
var chatService =
|
||||||
LazyServiceProvider.GetRequiredKeyedService<IChatCompletionService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IChatCompletionService>(modelDescribe.HandlerName);
|
||||||
|
|
||||||
var completeChatResponse = chatService.CompleteChatStreamAsync(modelDescribe,request, cancellationToken);
|
var completeChatResponse = chatService.CompleteChatStreamAsync(modelDescribe, request, cancellationToken);
|
||||||
var tokenUsage = new ThorUsageResponse();
|
var tokenUsage = new ThorUsageResponse();
|
||||||
|
|
||||||
//缓存队列算法
|
//缓存队列算法
|
||||||
@@ -301,8 +304,8 @@ public class AiGateWayManager : DomainService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图片生成
|
/// 图片生成
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -370,8 +373,8 @@ public class AiGateWayManager : DomainService
|
|||||||
throw new UserFriendlyException(errorContent);
|
throw new UserFriendlyException(errorContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 向量生成
|
/// 向量生成
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -483,7 +486,7 @@ public class AiGateWayManager : DomainService
|
|||||||
throw new UserFriendlyException(errorContent);
|
throw new UserFriendlyException(errorContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Anthropic聊天完成-非流式
|
/// Anthropic聊天完成-非流式
|
||||||
@@ -544,7 +547,7 @@ public class AiGateWayManager : DomainService
|
|||||||
await response.WriteAsJsonAsync(data, cancellationToken);
|
await response.WriteAsJsonAsync(data, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Anthropic聊天完成-缓存处理
|
/// Anthropic聊天完成-缓存处理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -568,13 +571,13 @@ public class AiGateWayManager : DomainService
|
|||||||
response.ContentType = "text/event-stream;charset=utf-8;";
|
response.ContentType = "text/event-stream;charset=utf-8;";
|
||||||
response.Headers.TryAdd("Cache-Control", "no-cache");
|
response.Headers.TryAdd("Cache-Control", "no-cache");
|
||||||
response.Headers.TryAdd("Connection", "keep-alive");
|
response.Headers.TryAdd("Connection", "keep-alive");
|
||||||
|
|
||||||
_specialCompatible.AnthropicCompatible(request);
|
_specialCompatible.AnthropicCompatible(request);
|
||||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Claude, request.Model);
|
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Claude, request.Model);
|
||||||
var chatService =
|
var chatService =
|
||||||
LazyServiceProvider.GetRequiredKeyedService<IAnthropicChatCompletionService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IAnthropicChatCompletionService>(modelDescribe.HandlerName);
|
||||||
|
|
||||||
var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe,request, cancellationToken);
|
var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe, request, cancellationToken);
|
||||||
ThorUsageResponse? tokenUsage = null;
|
ThorUsageResponse? tokenUsage = null;
|
||||||
StringBuilder backupSystemContent = new StringBuilder();
|
StringBuilder backupSystemContent = new StringBuilder();
|
||||||
try
|
try
|
||||||
@@ -655,10 +658,10 @@ public class AiGateWayManager : DomainService
|
|||||||
var chatService =
|
var chatService =
|
||||||
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
|
||||||
var data = await chatService.ResponsesAsync(modelDescribe, request, cancellationToken);
|
var data = await chatService.ResponsesAsync(modelDescribe, request, cancellationToken);
|
||||||
|
|
||||||
data.SupplementalMultiplier(modelDescribe.Multiplier);
|
data.SupplementalMultiplier(modelDescribe.Multiplier);
|
||||||
|
|
||||||
var tokenUsage= new ThorUsageResponse
|
var tokenUsage = new ThorUsageResponse
|
||||||
{
|
{
|
||||||
InputTokens = data.Usage.InputTokens,
|
InputTokens = data.Usage.InputTokens,
|
||||||
OutputTokens = data.Usage.OutputTokens,
|
OutputTokens = data.Usage.OutputTokens,
|
||||||
@@ -673,7 +676,7 @@ public class AiGateWayManager : DomainService
|
|||||||
ModelId = request.Model,
|
ModelId = request.Model,
|
||||||
TokenUsage = tokenUsage,
|
TokenUsage = tokenUsage,
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
|
|
||||||
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
@@ -681,9 +684,9 @@ public class AiGateWayManager : DomainService
|
|||||||
ModelId = request.Model,
|
ModelId = request.Model,
|
||||||
TokenUsage = tokenUsage
|
TokenUsage = tokenUsage
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
|
|
||||||
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage, tokenId);
|
||||||
|
|
||||||
// 扣减尊享token包用量
|
// 扣减尊享token包用量
|
||||||
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||||
if (totalTokens > 0)
|
if (totalTokens > 0)
|
||||||
@@ -694,8 +697,8 @@ public class AiGateWayManager : DomainService
|
|||||||
|
|
||||||
await response.WriteAsJsonAsync(data, cancellationToken);
|
await response.WriteAsJsonAsync(data, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OpenAi响应-流式-缓存处理
|
/// OpenAi响应-流式-缓存处理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -719,12 +722,12 @@ public class AiGateWayManager : DomainService
|
|||||||
response.ContentType = "text/event-stream;charset=utf-8;";
|
response.ContentType = "text/event-stream;charset=utf-8;";
|
||||||
response.Headers.TryAdd("Cache-Control", "no-cache");
|
response.Headers.TryAdd("Cache-Control", "no-cache");
|
||||||
response.Headers.TryAdd("Connection", "keep-alive");
|
response.Headers.TryAdd("Connection", "keep-alive");
|
||||||
|
|
||||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Response, request.Model);
|
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Response, request.Model);
|
||||||
var chatService =
|
var chatService =
|
||||||
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
|
||||||
|
|
||||||
var completeChatResponse = chatService.ResponsesStreamAsync(modelDescribe,request, cancellationToken);
|
var completeChatResponse = chatService.ResponsesStreamAsync(modelDescribe, request, cancellationToken);
|
||||||
ThorUsageResponse? tokenUsage = null;
|
ThorUsageResponse? tokenUsage = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -734,20 +737,20 @@ public class AiGateWayManager : DomainService
|
|||||||
if (responseResult.Item1.Contains("response.completed"))
|
if (responseResult.Item1.Contains("response.completed"))
|
||||||
{
|
{
|
||||||
var obj = responseResult.Item2!.Value;
|
var obj = responseResult.Item2!.Value;
|
||||||
int inputTokens = obj.GetPath("response","usage","input_tokens").GetInt();
|
int inputTokens = obj.GetPath("response", "usage", "input_tokens").GetInt();
|
||||||
int outputTokens = obj.GetPath("response","usage","output_tokens").GetInt();
|
int outputTokens = obj.GetPath("response", "usage", "output_tokens").GetInt();
|
||||||
inputTokens=Convert.ToInt32(inputTokens * modelDescribe.Multiplier);
|
inputTokens = Convert.ToInt32(inputTokens * modelDescribe.Multiplier);
|
||||||
outputTokens=Convert.ToInt32(outputTokens * modelDescribe.Multiplier);
|
outputTokens = Convert.ToInt32(outputTokens * modelDescribe.Multiplier);
|
||||||
tokenUsage = new ThorUsageResponse
|
tokenUsage = new ThorUsageResponse
|
||||||
{
|
{
|
||||||
PromptTokens =inputTokens,
|
PromptTokens = inputTokens,
|
||||||
InputTokens = inputTokens,
|
InputTokens = inputTokens,
|
||||||
OutputTokens = outputTokens,
|
OutputTokens = outputTokens,
|
||||||
CompletionTokens = outputTokens,
|
CompletionTokens = outputTokens,
|
||||||
TotalTokens = inputTokens+outputTokens,
|
TotalTokens = inputTokens + outputTokens,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
await WriteAsEventStreamDataAsync(httpContext, responseResult.Item1, responseResult.Item2,
|
await WriteAsEventStreamDataAsync(httpContext, responseResult.Item1, responseResult.Item2,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -762,7 +765,7 @@ public class AiGateWayManager : DomainService
|
|||||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
Content = "不予存储" ,
|
Content = "不予存储",
|
||||||
ModelId = request.Model,
|
ModelId = request.Model,
|
||||||
TokenUsage = tokenUsage,
|
TokenUsage = tokenUsage,
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
@@ -770,7 +773,7 @@ public class AiGateWayManager : DomainService
|
|||||||
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
Content = "不予存储" ,
|
Content = "不予存储",
|
||||||
ModelId = request.Model,
|
ModelId = request.Model,
|
||||||
TokenUsage = tokenUsage
|
TokenUsage = tokenUsage
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
@@ -814,9 +817,9 @@ public class AiGateWayManager : DomainService
|
|||||||
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
||||||
var data = await chatService.GenerateContentAsync(modelDescribe, request, cancellationToken);
|
var data = await chatService.GenerateContentAsync(modelDescribe, request, cancellationToken);
|
||||||
|
|
||||||
var tokenUsage = GeminiGenerateContentAcquirer.GetUsage(data);
|
var tokenUsage = GeminiGenerateContentAcquirer.GetUsage(data);
|
||||||
tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier);
|
tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier);
|
||||||
|
|
||||||
if (userId is not null)
|
if (userId is not null)
|
||||||
{
|
{
|
||||||
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
|
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
|
||||||
@@ -826,7 +829,7 @@ public class AiGateWayManager : DomainService
|
|||||||
ModelId = modelId,
|
ModelId = modelId,
|
||||||
TokenUsage = tokenUsage,
|
TokenUsage = tokenUsage,
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
|
|
||||||
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
@@ -834,9 +837,9 @@ public class AiGateWayManager : DomainService
|
|||||||
ModelId = modelId,
|
ModelId = modelId,
|
||||||
TokenUsage = tokenUsage
|
TokenUsage = tokenUsage
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
|
|
||||||
await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, tokenUsage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, tokenUsage, tokenId);
|
||||||
|
|
||||||
// 扣减尊享token包用量
|
// 扣减尊享token包用量
|
||||||
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||||
if (totalTokens > 0)
|
if (totalTokens > 0)
|
||||||
@@ -874,23 +877,25 @@ public class AiGateWayManager : DomainService
|
|||||||
response.ContentType = "text/event-stream;charset=utf-8;";
|
response.ContentType = "text/event-stream;charset=utf-8;";
|
||||||
response.Headers.TryAdd("Cache-Control", "no-cache");
|
response.Headers.TryAdd("Cache-Control", "no-cache");
|
||||||
response.Headers.TryAdd("Connection", "keep-alive");
|
response.Headers.TryAdd("Connection", "keep-alive");
|
||||||
|
|
||||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.GenerateContent, modelId);
|
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.GenerateContent, modelId);
|
||||||
var chatService =
|
var chatService =
|
||||||
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
||||||
|
|
||||||
var completeChatResponse = chatService.GenerateContentStreamAsync(modelDescribe,request, cancellationToken);
|
var completeChatResponse = chatService.GenerateContentStreamAsync(modelDescribe, request, cancellationToken);
|
||||||
ThorUsageResponse? tokenUsage = null;
|
ThorUsageResponse? tokenUsage = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await foreach (var responseResult in completeChatResponse)
|
await foreach (var responseResult in completeChatResponse)
|
||||||
{
|
{
|
||||||
if ( responseResult!.Value.GetPath("candidates", 0, "finishReason").GetString() == "STOP")
|
if (responseResult!.Value.GetPath("candidates", 0, "finishReason").GetString() == "STOP")
|
||||||
{
|
{
|
||||||
tokenUsage = GeminiGenerateContentAcquirer.GetUsage(responseResult!.Value);
|
tokenUsage = GeminiGenerateContentAcquirer.GetUsage(responseResult!.Value);
|
||||||
tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier);
|
tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier);
|
||||||
}
|
}
|
||||||
await response.WriteAsync($"data: {JsonSerializer.Serialize(responseResult)}\n\n", Encoding.UTF8, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
await response.WriteAsync($"data: {JsonSerializer.Serialize(responseResult)}\n\n", Encoding.UTF8,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
await response.Body.FlushAsync(cancellationToken).ConfigureAwait(false);
|
await response.Body.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -904,7 +909,7 @@ public class AiGateWayManager : DomainService
|
|||||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
Content = "不予存储" ,
|
Content = "不予存储",
|
||||||
ModelId = modelId,
|
ModelId = modelId,
|
||||||
TokenUsage = tokenUsage,
|
TokenUsage = tokenUsage,
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
@@ -912,7 +917,7 @@ public class AiGateWayManager : DomainService
|
|||||||
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
||||||
new MessageInputDto
|
new MessageInputDto
|
||||||
{
|
{
|
||||||
Content = "不予存储" ,
|
Content = "不予存储",
|
||||||
ModelId = modelId,
|
ModelId = modelId,
|
||||||
TokenUsage = tokenUsage
|
TokenUsage = tokenUsage
|
||||||
}, tokenId);
|
}, tokenId);
|
||||||
@@ -929,9 +934,102 @@ public class AiGateWayManager : DomainService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gemini 生成(Image)-非流式-缓存处理
|
||||||
|
/// 返回图片绝对路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskId"></param>
|
||||||
|
/// <param name="modelId"></param>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="userId"></param>
|
||||||
|
/// <param name="sessionId"></param>
|
||||||
|
/// <param name="tokenId"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
public async Task GeminiGenerateContentImageForStatisticsAsync(
|
||||||
|
Guid taskId,
|
||||||
|
string modelId,
|
||||||
|
JsonElement request,
|
||||||
|
Guid userId,
|
||||||
|
Guid? sessionId = null,
|
||||||
|
Guid? tokenId = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var imageStoreTask = await _imageStoreTaskRepository.GetFirstAsync(x => x.Id == taskId);
|
||||||
|
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.GenerateContent, modelId);
|
||||||
|
|
||||||
|
var chatService =
|
||||||
|
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
||||||
|
var data = await chatService.GenerateContentAsync(modelDescribe, request, cancellationToken);
|
||||||
|
|
||||||
|
//解析json,获取base64字符串
|
||||||
|
var imageBase64 = GeminiGenerateContentAcquirer.GetImageBase64(data);
|
||||||
|
|
||||||
|
//base64字符串存储,返回绝对路径,用于最后存储
|
||||||
|
var storeUrl = Base64ToPng(imageBase64, "存储的路径?这个放什么");
|
||||||
|
|
||||||
|
|
||||||
|
var tokenUsage = new ThorUsageResponse
|
||||||
|
{
|
||||||
|
InputTokens = (int)modelDescribe.Multiplier,
|
||||||
|
OutputTokens = (int)modelDescribe.Multiplier,
|
||||||
|
TotalTokens = (int)modelDescribe.Multiplier,
|
||||||
|
};
|
||||||
|
|
||||||
|
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
||||||
|
new MessageInputDto
|
||||||
|
{
|
||||||
|
Content = "不予存储",
|
||||||
|
ModelId = modelId,
|
||||||
|
TokenUsage = tokenUsage
|
||||||
|
}, tokenId);
|
||||||
|
|
||||||
|
await _usageStatisticsManager.SetUsageAsync(userId, modelId, tokenUsage, tokenId);
|
||||||
|
|
||||||
|
// 扣减尊享token包用量
|
||||||
|
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||||
|
if (totalTokens > 0)
|
||||||
|
{
|
||||||
|
await PremiumPackageManager.TryConsumeTokensAsync(userId, totalTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置存储url
|
||||||
|
imageStoreTask.SetSuccess(storeUrl);
|
||||||
|
await _imageStoreTaskRepository.UpdateAsync(imageStoreTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 Base64 字符串转换为 PNG 图片并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="base64String">Base64 字符串(可以包含或不包含 data:image/png;base64, 前缀)</param>
|
||||||
|
/// <param name="outputPath">输出文件路径</param>
|
||||||
|
private string Base64ToPng(string base64String, string outputPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 移除可能存在的 data URI scheme 前缀
|
||||||
|
if (base64String.Contains(","))
|
||||||
|
{
|
||||||
|
base64String = base64String.Substring(base64String.IndexOf(",") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 base64 字符串转换为字节数组
|
||||||
|
byte[] imageBytes = Convert.FromBase64String(base64String);
|
||||||
|
|
||||||
|
// 将字节数组写入文件
|
||||||
|
File.WriteAllBytes(outputPath, imageBytes);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new UserFriendlyException("gemini Base64转图像失败", innerException: ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo
|
||||||
|
//路径拼接一下?
|
||||||
|
return outputPath;
|
||||||
|
}
|
||||||
|
|
||||||
#region 流式传输格式Http响应
|
#region 流式传输格式Http响应
|
||||||
|
|
||||||
private static readonly byte[] EventPrefix = "event: "u8.ToArray();
|
private static readonly byte[] EventPrefix = "event: "u8.ToArray();
|
||||||
|
|||||||
Reference in New Issue
Block a user