using Dpz.Core.Entity.Base;
using Markdig;
using Markdig.Syntax;

namespace Dpz.Core.Service.Network.Models;

/// <summary>
/// Chat Completion API 响应
/// </summary>
[Obsolete("use IOpenAiService")]
public class ChatCompletionResponse
{
    [JsonPropertyName("id")]
    public string Id { get; set; } = string.Empty;

    [JsonPropertyName("object")]
    public string Object { get; set; } = string.Empty;

    [JsonPropertyName("created")]
    public long Created { get; set; }

    [JsonPropertyName("model")]
    public string Model { get; set; } = string.Empty;

    [JsonPropertyName("provider")]
    public string? Provider { get; set; }

    [JsonPropertyName("choices")]
    public List<CompletionChoice> Choices { get; set; } = [];

    [JsonPropertyName("usage")]
    public Usage? Usage { get; set; }

    public ResponseResult<T?> Analyze<T>()
        where T : new()
    {
        var result = new ResponseResult<T?>();
        T? t;

        var content = Choices.Count > 0 ? Choices[0].Message.Content : "";
        try
        {
            var pipeline = new MarkdownPipelineBuilder()
                .UseAutoLinks()
                .UsePipeTables()
                .UseTaskLists()
                .UseEmphasisExtras()
                .UseAutoIdentifiers()
                .Build();
            var document = Markdown.Parse(content, pipeline);
            var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };

            var codeBlocks = document.Descendants<CodeBlock>().ToList();
            var jsonText = ExtractJsonFromCodeBlocks(codeBlocks);
            jsonText ??= ExtractFirstJsonPayload(content);
            jsonText ??= content;

            t = JsonSerializer.Deserialize<T>(jsonText, options);
        }
        catch (Exception e)
        {
            result.Arguments = [content];
            return result.WithFail(e.Message);
        }
        return result.WithOk(t);
    }

    private static string? ExtractJsonFromCodeBlocks(IEnumerable<CodeBlock> codeBlocks)
    {
        var blocks = codeBlocks.ToList();
        if (blocks.Count == 0)
        {
            return null;
        }

        foreach (var codeBlock in blocks)
        {
            var fenced = codeBlock as FencedCodeBlock;
            var info = fenced?.Info ?? string.Empty;
            if (info.Trim().Equals("json", StringComparison.OrdinalIgnoreCase))
            {
                var text = codeBlock.Lines.ToString();
                if (!string.IsNullOrWhiteSpace(text))
                {
                    return text;
                }
            }
        }

        foreach (var codeBlock in blocks)
        {
            var text = codeBlock.Lines.ToString();
            if (!string.IsNullOrWhiteSpace(text))
            {
                return text;
            }
        }

        return null;
    }

    private static string? ExtractFirstJsonPayload(string content)
    {
        if (string.IsNullOrWhiteSpace(content))
        {
            return null;
        }

        var trimmed = content.Trim();
        var objectIndex = trimmed.IndexOf('{');
        var arrayIndex = trimmed.IndexOf('[');

        var startIndex = -1;
        if (objectIndex >= 0 && arrayIndex >= 0)
        {
            startIndex = Math.Min(objectIndex, arrayIndex);
        }
        else if (objectIndex >= 0)
        {
            startIndex = objectIndex;
        }
        else if (arrayIndex >= 0)
        {
            startIndex = arrayIndex;
        }

        if (startIndex < 0)
        {
            return null;
        }

        var candidate = trimmed[startIndex..];
        for (var length = candidate.Length; length > 0; length--)
        {
            var slice = candidate[..length];
            try
            {
                JsonDocument.Parse(slice);
                return slice;
            }
            catch (JsonException)
            {
                // ignore
            }
        }

        return null;
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个用于处理 OpenAI Chat Completion API 响应的 C# 模型类,主要功能是解析 API 响应并从中提取结构化数据。

主要组成部分

1. 类定义与属性

[Obsolete("use IOpenAiService")]
public class ChatCompletionResponse
  • 标记为已过时,建议使用 IOpenAiService
  • 包含 Chat Completion API 的标准响应字段:
    • Id: 响应唯一标识
    • Object: 对象类型
    • Created: 创建时间戳
    • Model: 使用的模型名称
    • Provider: 提供商(可选)
    • Choices: 生成的回复选项列表
    • Usage: 使用情况统计

2. 核心方法:Analyze<T>()

这是类的核心功能,用于从 AI 响应中提取并反序列化 JSON 数据:

工作流程:

  1. 获取第一个选项的消息内容
  2. 使用 Markdig 解析 Markdown 格式
  3. 按优先级尝试提取 JSON:
    • 首先从代码块(code blocks)中提取
    • 如果失败,尝试从原始内容中提取 JSON
    • 最后使用完整内容
  4. 将提取的 JSON 反序列化为泛型类型 T
  5. 返回 ResponseResult<T?> 包装的结果

3. 辅助方法:ExtractJsonFromCodeBlocks()

从 Markdown 代码块中提取 JSON:

  • 优先查找标记为 json 的代码块
  • 如果没有,返回第一个非空代码块内容
  • 如果都没有,返回 null

4. 辅助方法:ExtractFirstJsonPayload()

从普通文本中智能提取 JSON 数据:

  • 查找第一个 {[ 字符的位置
  • 从该位置开始,逐步尝试解析不同长度的子串
  • 使用 JsonDocument.Parse() 验证 JSON 有效性
  • 返回第一个有效的 JSON 字符串

使用场景

这个类特别适用于处理 AI 返回的非结构化文本,例如:

这是一些解释文字...

```json
{
  "name": "John",
  "age": 30
}

更多说明...


能够智能提取其中的 JSON 数据并转换为强类型对象,增强了与 AI API 交互的健壮性。
评论加载中...