< Summary

Information
Class: Elsa.AI.Host.Services.AIGroundingResultFormatter
Assembly: Elsa.AI.Host
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.AI.Host/Services/AIGroundingResultFormatter.cs
Line coverage
76%
Covered lines: 54
Uncovered lines: 17
Coverable lines: 71
Total lines: 108
Line coverage: 76%
Branch coverage
88%
Covered branches: 23
Total branches: 26
Branch coverage: 88.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.cctor()100%11100%
CreateResult(...)80%101095.45%
Unavailable(...)100%210%
RedactObject(...)100%44100%
LimitData(...)75%8437.5%
RedactNode(...)100%66100%
RedactArray(...)100%22100%
IsSensitiveKey(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.AI.Host/Services/AIGroundingResultFormatter.cs

#LineLine coverage
 1using System.Text.Json;
 2using Elsa.AI.Abstractions.Models;
 3using Elsa.AI.Host.Options;
 4using Microsoft.Extensions.Options;
 5
 6namespace Elsa.AI.Host.Services;
 7
 508public class AIGroundingResultFormatter(IOptions<AIHostOptions> options)
 9{
 210    private static readonly string[] SensitiveNameFragments =
 211    [
 212        "password",
 213        "secret",
 214        "token",
 215        "apikey",
 216        "api_key",
 217        "authorization",
 218        "connectionstring",
 219        "connection_string",
 220        "credential"
 221    ];
 22
 23    public AIToolResult CreateResult(string summary, IEnumerable<JsonObject> items, int total, IEnumerable<string>? evid
 24    {
 725        var maxItems = Math.Max(0, options.Value.Grounding.MaxItems);
 726        var itemList = items
 727            .Take(maxItems)
 728            .Select(x => RedactObject((JsonObject)x.DeepClone()))
 729            .ToList();
 730        var truncated = total > itemList.Count;
 731        var groundingResult = new AIGroundingToolResult
 732        {
 733            Summary = summary,
 734            Items = itemList,
 735            Total = total,
 736            Returned = itemList.Count,
 737            Truncated = truncated,
 538            Evidence = evidence?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).To
 039            Warnings = warnings?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).To
 740        };
 41
 742        var data = AIGroundingJson.ToJsonObject(groundingResult);
 743        return new AIToolResult
 744        {
 745            Summary = truncated ? $"{summary} Returned {itemList.Count} of {total} results." : summary,
 746            Data = LimitData(data)
 747        };
 48    }
 49
 50    public AIToolResult Unavailable(string sourceName) =>
 051        new()
 052        {
 053            Status = AIToolInvocationStatus.Failed,
 054            Summary = $"{sourceName} is not available in this Elsa host.",
 055            Error = $"{sourceName} is not available.",
 056            Data = new JsonObject
 057            {
 058                ["available"] = false,
 059                ["source"] = sourceName
 060            }
 061        };
 62
 63    public JsonObject RedactObject(JsonObject source)
 64    {
 1865        var redacted = new JsonObject();
 23066        foreach (var (key, value) in source)
 9767            redacted[key] = IsSensitiveKey(key) ? "***" : RedactNode(value);
 68
 1869        return redacted;
 70    }
 71
 72    public JsonObject LimitData(JsonObject data)
 73    {
 774        var maxBytes = Math.Max(0, options.Value.Grounding.MaxResultBytes);
 775        if (maxBytes == 0 || JsonSerializer.SerializeToUtf8Bytes(data).Length <= maxBytes)
 776            return data;
 77
 078        return new JsonObject
 079        {
 080            ["truncated"] = true,
 081            ["maxBytes"] = maxBytes
 082        };
 83    }
 84
 85    private JsonNode? RedactNode(JsonNode? node) =>
 10186        node switch
 10187        {
 1088            JsonObject jsonObject => RedactObject(jsonObject),
 1989            JsonArray jsonArray => RedactArray(jsonArray),
 1190            null => null,
 6191            _ => node.DeepClone()
 10192        };
 93
 94    private JsonArray RedactArray(JsonArray source)
 95    {
 1996        var redacted = new JsonArray();
 5297        foreach (var value in source)
 798            redacted.Add(RedactNode(value));
 99
 19100        return redacted;
 101    }
 102
 103    private static bool IsSensitiveKey(string key)
 104    {
 97105        var normalized = key.Replace("-", "", StringComparison.Ordinal).Replace("_", "", StringComparison.Ordinal);
 949106        return SensitiveNameFragments.Any(x => normalized.Contains(x, StringComparison.OrdinalIgnoreCase));
 107    }
 108}