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 { 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, }; } /// /// 获取图片 base64(包含 data:image 前缀) /// 优先从 inlineData.data 中获取,其次从 markdown text 中解析 /// public static string GetImagePrefixBase64(JsonElement response) { // 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) var startMarker = "(data:image/"; var startIndex = text.IndexOf(startMarker, StringComparison.Ordinal); if (startIndex < 0) { return string.Empty; } startIndex += 1; // 跳过 "(" var endIndex = text.IndexOf(')', startIndex); if (endIndex <= startIndex) { return string.Empty; } return text.Substring(startIndex, endIndex - startIndex); } }