From 5a7f0ab1086dc28f584b04f63e3e50061ce7946c Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 03:19:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=9B=BE=E7=89=87=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Jobs/ImageGenerationJob.cs | 2 +- .../Gemini/GeminiGenerateContentAcquirer.cs | 32 ++++++++++++------- .../Managers/AiGateWayManager.cs | 6 ++-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs index c1c81660..062b6e31 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs @@ -43,7 +43,7 @@ public class ImageGenerationJob : AsyncBackgroundJob, IT // 构建 Gemini API 请求对象 var parts = new List { - new { text = task.Prompt } + new { role="user",text = task.Prompt } }; // 添加参考图(如果有) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Gemini/GeminiGenerateContentAcquirer.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Gemini/GeminiGenerateContentAcquirer.cs index c678013e..d0dfbf81 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Gemini/GeminiGenerateContentAcquirer.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Gemini/GeminiGenerateContentAcquirer.cs @@ -20,7 +20,6 @@ public static class GeminiGenerateContentAcquirer + usage.Value.GetPath("thoughtsTokenCount").GetInt() + usage.Value.GetPath("toolUsePromptTokenCount").GetInt(); - return new ThorUsageResponse { PromptTokens = inputTokens, @@ -32,32 +31,43 @@ public static class GeminiGenerateContentAcquirer } /// - /// 获取图片url,包含前缀 + /// 获取图片 base64(包含 data:image 前缀) + /// 优先从 inlineData.data 中获取,其次从 markdown text 中解析 /// - /// - /// public static string GetImagePrefixBase64(JsonElement response) { - // 获取 candidates[0].content.parts[0].text - var text = response.GetPath("candidates", 0, "content", "parts", 0, "text").GetString(); + // Step 1: 优先尝试从 candidates[0].content.parts[0].inlineData.data 获取 + var inlineBase64 = response + .GetPath("candidates", 0, "content", "parts", 0, "inlineData", "data") + .GetString(); + + if (!string.IsNullOrEmpty(inlineBase64)) + { + // 默认按 png 格式拼接前缀 + return $"data:image/png;base64,{inlineBase64}"; + } + + // Step 2: fallback,从 candidates[0].content.parts[0].text 中解析 markdown 图片 + var text = response + .GetPath("candidates", 0, "content", "parts", 0, "text") + .GetString(); + if (string.IsNullOrEmpty(text)) { return string.Empty; } - // 解析 markdown 图片格式: ![image](data:image/png;base64,xxx) - // 提取括号内的 data:image/xxx;base64,xxx 部分 + // markdown 图片格式: ![image](data:image/png;base64,xxx) var startMarker = "(data:image/"; - var startIndex = text.IndexOf(startMarker); + var startIndex = text.IndexOf(startMarker, StringComparison.Ordinal); if (startIndex < 0) { return string.Empty; } - // 从 "data:" 开始 startIndex += 1; // 跳过 "(" var endIndex = text.IndexOf(')', startIndex); - if (endIndex < 0) + if (endIndex <= startIndex) { return string.Empty; } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs index 3ea535d8..b7eb5177 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs @@ -92,7 +92,7 @@ public class AiGateWayManager : DomainService { throw new UserFriendlyException($"【{modelId}】模型当前版本【{modelApiType}】格式不支持"); } - // ✅ 统一处理 -nx 后缀(网关层模型规范化) + // ✅ 统一处理 yi- 后缀(网关层模型规范化) if (!string.IsNullOrEmpty(aiModelDescribe.ModelId) && aiModelDescribe.ModelId.StartsWith("yi-", StringComparison.OrdinalIgnoreCase)) { @@ -1005,13 +1005,13 @@ public class AiGateWayManager : DomainService var data = await chatService.GenerateContentAsync(modelDescribe, request, cancellationToken); //解析json,获取base64字符串 - var imageBase64 = GeminiGenerateContentAcquirer.GetImagePrefixBase64(data); + var imagePrefixBase64 = GeminiGenerateContentAcquirer.GetImagePrefixBase64(data); //远程调用上传接口,将base64转换为URL var httpClient = LazyServiceProvider.LazyGetRequiredService().CreateClient(); // var uploadUrl = $"https://ccnetcore.com/prod-api/ai-hub/ai-image/upload-base64"; var uploadUrl = $"{ImageStoreHost}/ai-image/upload-base64"; - var content = new StringContent(JsonSerializer.Serialize(imageBase64), Encoding.UTF8, "application/json"); + var content = new StringContent(JsonSerializer.Serialize(imagePrefixBase64), Encoding.UTF8, "application/json"); var uploadResponse = await httpClient.PostAsync(uploadUrl, content, cancellationToken); uploadResponse.EnsureSuccessStatusCode(); var storeUrl = await uploadResponse.Content.ReadAsStringAsync(cancellationToken);