Files
Yi.Framework/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Dtos/Gemini/GeminiGenerateContentAcquirer.cs
ccnetcore a67af0485e fix: 优化 Gemini 图片 base64 获取逻辑
从最后一个 part 逆序查找 inlineData 和 text,避免只读取首个 part 导致图片缺失
支持根据 inlineData.mimeType 动态生成 data:image 前缀
增强对多 part 返回结构的兼容性,提高图片解析成功率
2026-01-11 17:27:57 +08:00

166 lines
5.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.Json;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.Shared.Extensions;
namespace Yi.Framework.AiHub.Domain.Shared.Dtos.Gemini;
public static class GeminiGenerateContentAcquirer
{
/// <summary>
/// 从请求体中提取用户最后一条消息内容
/// 路径: contents[last].parts[last].text
/// </summary>
public static string GetLastUserContent(JsonElement request)
{
var contents = request.GetPath("contents");
if (!contents.HasValue || contents.Value.ValueKind != JsonValueKind.Array)
{
return string.Empty;
}
var contentsArray = contents.Value.EnumerateArray().ToList();
if (contentsArray.Count == 0)
{
return string.Empty;
}
var lastContent = contentsArray[^1];
var parts = lastContent.GetPath("parts");
if (!parts.HasValue || parts.Value.ValueKind != JsonValueKind.Array)
{
return string.Empty;
}
var partsArray = parts.Value.EnumerateArray().ToList();
if (partsArray.Count == 0)
{
return string.Empty;
}
// 获取最后一个 part 的 text
var lastPart = partsArray[^1];
return lastPart.GetPath("text").GetString() ?? string.Empty;
}
/// <summary>
/// 从响应中提取文本内容(非 thought 类型)
/// 路径: candidates[0].content.parts[].text (where thought != true)
/// </summary>
public static string GetTextContent(JsonElement response)
{
var candidates = response.GetPath("candidates");
if (!candidates.HasValue || candidates.Value.ValueKind != JsonValueKind.Array)
{
return string.Empty;
}
var candidatesArray = candidates.Value.EnumerateArray().ToList();
if (candidatesArray.Count == 0)
{
return string.Empty;
}
var parts = candidatesArray[0].GetPath("content", "parts");
if (!parts.HasValue || parts.Value.ValueKind != JsonValueKind.Array)
{
return string.Empty;
}
// 遍历所有 parts只取非 thought 的 text
foreach (var part in parts.Value.EnumerateArray())
{
var isThought = part.GetPath("thought").GetBool();
if (!isThought)
{
var text = part.GetPath("text").GetString();
if (!string.IsNullOrEmpty(text))
{
return text;
}
}
}
return string.Empty;
}
public static ThorUsageResponse? GetUsage(JsonElement response)
{
var usage = response.GetPath("usageMetadata");
if (!usage.HasValue)
{
return null;
}
var inputTokens = usage.Value.GetPath("promptTokenCount").GetInt();
var outputTokens = usage.Value.GetPath("candidatesTokenCount").GetInt()
+ usage.Value.GetPath("cachedContentTokenCount").GetInt()
+ usage.Value.GetPath("thoughtsTokenCount").GetInt()
+ usage.Value.GetPath("toolUsePromptTokenCount").GetInt();
return new ThorUsageResponse
{
PromptTokens = inputTokens,
InputTokens = inputTokens,
OutputTokens = outputTokens,
CompletionTokens = outputTokens,
TotalTokens = inputTokens + outputTokens,
};
}
/// <summary>
/// 获取图片 base64包含 data:image 前缀)
/// 从最后一个 part 开始查找 inlineData找不到再从最后一个 part 开始查找 text
/// </summary>
public static string GetImagePrefixBase64(JsonElement response)
{
var parts = response.GetPath("candidates", 0, "content", "parts");
if (!parts.HasValue || parts.Value.ValueKind != JsonValueKind.Array)
{
return string.Empty;
}
var partsArray = parts.Value.EnumerateArray().ToList();
if (partsArray.Count == 0)
{
return string.Empty;
}
// Step 1: 从最后一个 part 开始查找 inlineData
for (int i = partsArray.Count - 1; i >= 0; i--)
{
var inlineBase64 = partsArray[i].GetPath("inlineData", "data").GetString();
if (!string.IsNullOrEmpty(inlineBase64))
{
var mimeType = partsArray[i].GetPath("inlineData", "mimeType").GetString() ?? "image/png";
return $"data:{mimeType};base64,{inlineBase64}";
}
}
// Step 2: 从最后一个 part 开始查找 text 中的 markdown 图片
for (int i = partsArray.Count - 1; i >= 0; i--)
{
var text = partsArray[i].GetPath("text").GetString();
if (string.IsNullOrEmpty(text))
{
continue;
}
// markdown 图片格式: ![image](data:image/png;base64,xxx)
var startMarker = "(data:image/";
var startIndex = text.IndexOf(startMarker, StringComparison.Ordinal);
if (startIndex < 0)
{
continue;
}
startIndex += 1; // 跳过 "("
var endIndex = text.IndexOf(')', startIndex);
if (endIndex > startIndex)
{
return text.Substring(startIndex, endIndex - startIndex);
}
}
return string.Empty;
}
}