feat: 准备构建图片生成
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
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.Extensions;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
@@ -30,11 +31,11 @@ public class OpenApiService : ApplicationService
|
||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly PremiumPackageManager _premiumPackageManager;
|
||||
|
||||
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreRepository;
|
||||
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
||||
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
||||
ISqlSugarRepository<AiModelEntity> aiModelRepository, AiBlacklistManager aiBlacklistManager,
|
||||
IAccountService accountService, PremiumPackageManager premiumPackageManager)
|
||||
IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreRepository)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
@@ -44,6 +45,7 @@ public class OpenApiService : ApplicationService
|
||||
_aiBlacklistManager = aiBlacklistManager;
|
||||
_accountService = accountService;
|
||||
_premiumPackageManager = premiumPackageManager;
|
||||
_imageStoreRepository = imageStoreRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -259,11 +261,13 @@ public class OpenApiService : ApplicationService
|
||||
/// 生成-Gemini (尊享服务专用)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="isAsync"></param>
|
||||
/// <param name="modelId"></param>
|
||||
/// <param name="alt"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
[HttpPost("openApi/v1beta/models/{modelId}:{action:regex(^(generateContent|streamGenerateContent)$)}")]
|
||||
public async Task GenerateContentAsync([FromBody] JsonElement input,
|
||||
[FromQuery] bool isAsync,
|
||||
[FromRoute] string modelId,
|
||||
[FromQuery] string? alt, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -294,6 +298,18 @@ public class OpenApiService : ApplicationService
|
||||
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
|
||||
if (alt == "sse")
|
||||
{
|
||||
|
||||
@@ -30,4 +30,16 @@ public static class GeminiGenerateContentAcquirer
|
||||
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 Yi.Framework.AiHub.Domain.AiGateWay;
|
||||
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.Shared.Consts;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Dtos;
|
||||
@@ -39,11 +40,12 @@ public class AiGateWayManager : DomainService
|
||||
private readonly UsageStatisticsManager _usageStatisticsManager;
|
||||
private readonly ISpecialCompatible _specialCompatible;
|
||||
private PremiumPackageManager? _premiumPackageManager;
|
||||
|
||||
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreTaskRepository;
|
||||
|
||||
public AiGateWayManager(ISqlSugarRepository<AiAppAggregateRoot> aiAppRepository, ILogger<AiGateWayManager> logger,
|
||||
AiMessageManager aiMessageManager, UsageStatisticsManager usageStatisticsManager,
|
||||
ISpecialCompatible specialCompatible, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||||
ISpecialCompatible specialCompatible, ISqlSugarRepository<AiModelEntity> aiModelRepository,
|
||||
ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreTaskRepository)
|
||||
{
|
||||
_aiAppRepository = aiAppRepository;
|
||||
_logger = logger;
|
||||
@@ -51,6 +53,7 @@ public class AiGateWayManager : DomainService
|
||||
_usageStatisticsManager = usageStatisticsManager;
|
||||
_specialCompatible = specialCompatible;
|
||||
_aiModelRepository = aiModelRepository;
|
||||
_imageStoreTaskRepository = imageStoreTaskRepository;
|
||||
}
|
||||
|
||||
private PremiumPackageManager PremiumPackageManager =>
|
||||
@@ -93,7 +96,7 @@ public class AiGateWayManager : DomainService
|
||||
return aiModelDescribe;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 聊天完成-非流式
|
||||
/// </summary>
|
||||
@@ -155,7 +158,7 @@ public class AiGateWayManager : DomainService
|
||||
await response.WriteAsJsonAsync(data, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 聊天完成-缓存处理
|
||||
/// </summary>
|
||||
@@ -185,8 +188,8 @@ public class AiGateWayManager : DomainService
|
||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.OpenAi, request.Model);
|
||||
var chatService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<IChatCompletionService>(modelDescribe.HandlerName);
|
||||
|
||||
var completeChatResponse = chatService.CompleteChatStreamAsync(modelDescribe,request, cancellationToken);
|
||||
|
||||
var completeChatResponse = chatService.CompleteChatStreamAsync(modelDescribe, request, cancellationToken);
|
||||
var tokenUsage = new ThorUsageResponse();
|
||||
|
||||
//缓存队列算法
|
||||
@@ -301,8 +304,8 @@ public class AiGateWayManager : DomainService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 图片生成
|
||||
/// </summary>
|
||||
@@ -370,8 +373,8 @@ public class AiGateWayManager : DomainService
|
||||
throw new UserFriendlyException(errorContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 向量生成
|
||||
/// </summary>
|
||||
@@ -483,7 +486,7 @@ public class AiGateWayManager : DomainService
|
||||
throw new UserFriendlyException(errorContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Anthropic聊天完成-非流式
|
||||
@@ -544,7 +547,7 @@ public class AiGateWayManager : DomainService
|
||||
await response.WriteAsJsonAsync(data, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Anthropic聊天完成-缓存处理
|
||||
/// </summary>
|
||||
@@ -568,13 +571,13 @@ public class AiGateWayManager : DomainService
|
||||
response.ContentType = "text/event-stream;charset=utf-8;";
|
||||
response.Headers.TryAdd("Cache-Control", "no-cache");
|
||||
response.Headers.TryAdd("Connection", "keep-alive");
|
||||
|
||||
|
||||
_specialCompatible.AnthropicCompatible(request);
|
||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Claude, request.Model);
|
||||
var chatService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<IAnthropicChatCompletionService>(modelDescribe.HandlerName);
|
||||
|
||||
var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe,request, cancellationToken);
|
||||
|
||||
var completeChatResponse = chatService.StreamChatCompletionsAsync(modelDescribe, request, cancellationToken);
|
||||
ThorUsageResponse? tokenUsage = null;
|
||||
StringBuilder backupSystemContent = new StringBuilder();
|
||||
try
|
||||
@@ -655,10 +658,10 @@ public class AiGateWayManager : DomainService
|
||||
var chatService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
|
||||
var data = await chatService.ResponsesAsync(modelDescribe, request, cancellationToken);
|
||||
|
||||
|
||||
data.SupplementalMultiplier(modelDescribe.Multiplier);
|
||||
|
||||
var tokenUsage= new ThorUsageResponse
|
||||
|
||||
var tokenUsage = new ThorUsageResponse
|
||||
{
|
||||
InputTokens = data.Usage.InputTokens,
|
||||
OutputTokens = data.Usage.OutputTokens,
|
||||
@@ -673,7 +676,7 @@ public class AiGateWayManager : DomainService
|
||||
ModelId = request.Model,
|
||||
TokenUsage = tokenUsage,
|
||||
}, tokenId);
|
||||
|
||||
|
||||
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
@@ -681,9 +684,9 @@ public class AiGateWayManager : DomainService
|
||||
ModelId = request.Model,
|
||||
TokenUsage = tokenUsage
|
||||
}, tokenId);
|
||||
|
||||
|
||||
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage, tokenId);
|
||||
|
||||
|
||||
// 扣减尊享token包用量
|
||||
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||
if (totalTokens > 0)
|
||||
@@ -694,8 +697,8 @@ public class AiGateWayManager : DomainService
|
||||
|
||||
await response.WriteAsJsonAsync(data, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// OpenAi响应-流式-缓存处理
|
||||
/// </summary>
|
||||
@@ -719,12 +722,12 @@ public class AiGateWayManager : DomainService
|
||||
response.ContentType = "text/event-stream;charset=utf-8;";
|
||||
response.Headers.TryAdd("Cache-Control", "no-cache");
|
||||
response.Headers.TryAdd("Connection", "keep-alive");
|
||||
|
||||
|
||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.Response, request.Model);
|
||||
var chatService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<IOpenAiResponseService>(modelDescribe.HandlerName);
|
||||
|
||||
var completeChatResponse = chatService.ResponsesStreamAsync(modelDescribe,request, cancellationToken);
|
||||
|
||||
var completeChatResponse = chatService.ResponsesStreamAsync(modelDescribe, request, cancellationToken);
|
||||
ThorUsageResponse? tokenUsage = null;
|
||||
try
|
||||
{
|
||||
@@ -734,20 +737,20 @@ public class AiGateWayManager : DomainService
|
||||
if (responseResult.Item1.Contains("response.completed"))
|
||||
{
|
||||
var obj = responseResult.Item2!.Value;
|
||||
int inputTokens = obj.GetPath("response","usage","input_tokens").GetInt();
|
||||
int outputTokens = obj.GetPath("response","usage","output_tokens").GetInt();
|
||||
inputTokens=Convert.ToInt32(inputTokens * modelDescribe.Multiplier);
|
||||
outputTokens=Convert.ToInt32(outputTokens * modelDescribe.Multiplier);
|
||||
int inputTokens = obj.GetPath("response", "usage", "input_tokens").GetInt();
|
||||
int outputTokens = obj.GetPath("response", "usage", "output_tokens").GetInt();
|
||||
inputTokens = Convert.ToInt32(inputTokens * modelDescribe.Multiplier);
|
||||
outputTokens = Convert.ToInt32(outputTokens * modelDescribe.Multiplier);
|
||||
tokenUsage = new ThorUsageResponse
|
||||
{
|
||||
PromptTokens =inputTokens,
|
||||
PromptTokens = inputTokens,
|
||||
InputTokens = inputTokens,
|
||||
OutputTokens = outputTokens,
|
||||
CompletionTokens = outputTokens,
|
||||
TotalTokens = inputTokens+outputTokens,
|
||||
TotalTokens = inputTokens + outputTokens,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
await WriteAsEventStreamDataAsync(httpContext, responseResult.Item1, responseResult.Item2,
|
||||
cancellationToken);
|
||||
}
|
||||
@@ -762,7 +765,7 @@ public class AiGateWayManager : DomainService
|
||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
Content = "不予存储" ,
|
||||
Content = "不予存储",
|
||||
ModelId = request.Model,
|
||||
TokenUsage = tokenUsage,
|
||||
}, tokenId);
|
||||
@@ -770,7 +773,7 @@ public class AiGateWayManager : DomainService
|
||||
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
Content = "不予存储" ,
|
||||
Content = "不予存储",
|
||||
ModelId = request.Model,
|
||||
TokenUsage = tokenUsage
|
||||
}, tokenId);
|
||||
@@ -814,9 +817,9 @@ public class AiGateWayManager : DomainService
|
||||
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
||||
var data = await chatService.GenerateContentAsync(modelDescribe, request, cancellationToken);
|
||||
|
||||
var tokenUsage = GeminiGenerateContentAcquirer.GetUsage(data);
|
||||
var tokenUsage = GeminiGenerateContentAcquirer.GetUsage(data);
|
||||
tokenUsage.SetSupplementalMultiplier(modelDescribe.Multiplier);
|
||||
|
||||
|
||||
if (userId is not null)
|
||||
{
|
||||
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
|
||||
@@ -826,7 +829,7 @@ public class AiGateWayManager : DomainService
|
||||
ModelId = modelId,
|
||||
TokenUsage = tokenUsage,
|
||||
}, tokenId);
|
||||
|
||||
|
||||
await _aiMessageManager.CreateSystemMessageAsync(userId.Value, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
@@ -834,9 +837,9 @@ public class AiGateWayManager : DomainService
|
||||
ModelId = modelId,
|
||||
TokenUsage = tokenUsage
|
||||
}, tokenId);
|
||||
|
||||
|
||||
await _usageStatisticsManager.SetUsageAsync(userId.Value, modelId, tokenUsage, tokenId);
|
||||
|
||||
|
||||
// 扣减尊享token包用量
|
||||
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||
if (totalTokens > 0)
|
||||
@@ -874,23 +877,25 @@ public class AiGateWayManager : DomainService
|
||||
response.ContentType = "text/event-stream;charset=utf-8;";
|
||||
response.Headers.TryAdd("Cache-Control", "no-cache");
|
||||
response.Headers.TryAdd("Connection", "keep-alive");
|
||||
|
||||
|
||||
var modelDescribe = await GetModelAsync(ModelApiTypeEnum.GenerateContent, modelId);
|
||||
var chatService =
|
||||
LazyServiceProvider.GetRequiredKeyedService<IGeminiGenerateContentService>(modelDescribe.HandlerName);
|
||||
|
||||
var completeChatResponse = chatService.GenerateContentStreamAsync(modelDescribe,request, cancellationToken);
|
||||
|
||||
var completeChatResponse = chatService.GenerateContentStreamAsync(modelDescribe, request, cancellationToken);
|
||||
ThorUsageResponse? tokenUsage = null;
|
||||
try
|
||||
{
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
@@ -904,7 +909,7 @@ public class AiGateWayManager : DomainService
|
||||
await _aiMessageManager.CreateUserMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
Content = "不予存储" ,
|
||||
Content = "不予存储",
|
||||
ModelId = modelId,
|
||||
TokenUsage = tokenUsage,
|
||||
}, tokenId);
|
||||
@@ -912,7 +917,7 @@ public class AiGateWayManager : DomainService
|
||||
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId,
|
||||
new MessageInputDto
|
||||
{
|
||||
Content = "不予存储" ,
|
||||
Content = "不予存储",
|
||||
ModelId = modelId,
|
||||
TokenUsage = tokenUsage
|
||||
}, 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响应
|
||||
|
||||
private static readonly byte[] EventPrefix = "event: "u8.ToArray();
|
||||
|
||||
Reference in New Issue
Block a user