feat: 流式处理统一返回用户/系统内容并完善消息存储

引入 StreamProcessResult 统一封装流式处理结果,补充各 API 类型下用户输入与系统输出内容的提取与累计,用于会话消息持久化与用量统计;同时增强 Gemini 请求与响应内容解析能力,确保流式场景下消息与 token 使用数据完整一致。
This commit is contained in:
ccnetcore
2026-01-11 13:48:20 +08:00
parent 6f316d3e51
commit 5e37859157
2 changed files with 205 additions and 22 deletions

View File

@@ -6,6 +6,83 @@ 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");