< Summary

Information
Class: Intentum.AI.Catalog.IntentCatalog
Assembly: Intentum.AI
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.AI/Catalog/IntentCatalog.cs
Line coverage
100%
Covered lines: 61
Uncovered lines: 0
Coverable lines: 61
Total lines: 115
Line coverage: 100%
Branch coverage
94%
Covered branches: 34
Total branches: 36
Branch coverage: 94.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%11100%
Add(...)100%22100%
Define(...)100%11100%
ResolveEmbeddingsAsync()93.75%1616100%
GetEmbedding(...)100%22100%
FindBestMatch(...)90%1010100%
AverageVectors(...)100%66100%

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.AI/Catalog/IntentCatalog.cs

#LineLine coverage
 1using Intentum.AI.Embeddings;
 2
 3namespace Intentum.AI.Catalog;
 4
 5/// <summary>
 6/// Registry of known intent definitions with their reference embeddings.
 7/// Used by CatalogIntentClassifier to perform nearest-neighbor intent classification.
 8/// </summary>
 9public sealed class IntentCatalog
 10{
 1511    private readonly List<IntentDefinition> _definitions = [];
 1512    private readonly Dictionary<string, IReadOnlyList<double>> _resolvedEmbeddings = new(StringComparer.OrdinalIgnoreCas
 13
 814    public IReadOnlyList<IntentDefinition> Definitions => _definitions;
 15
 16    /// <summary>
 17    /// Adds an intent definition to the catalog.
 18    /// </summary>
 19    public IntentCatalog Add(IntentDefinition definition)
 420    {
 421        _definitions.Add(definition ?? throw new ArgumentNullException(nameof(definition)));
 322        return this;
 323    }
 24
 25    /// <summary>
 26    /// Adds an intent definition using a fluent builder pattern.
 27    /// </summary>
 28    public IntentCatalog Define(string name, string description, params string[] exampleBehaviorKeys)
 1429    {
 1430        _definitions.Add(new IntentDefinition(name, description, exampleBehaviorKeys));
 1431        return this;
 1432    }
 33
 34    /// <summary>
 35    /// Resolves reference embeddings for all definitions using the given provider.
 36    /// For each definition, the embedding is computed by averaging the embeddings of all example behavior keys.
 37    /// </summary>
 38    public async Task ResolveEmbeddingsAsync(
 39        IIntentEmbeddingProvider provider,
 40        CancellationToken cancellationToken = default)
 841    {
 842        _resolvedEmbeddings.Clear();
 43
 4644        foreach (var definition in _definitions)
 1145        {
 1146            if (definition.ReferenceEmbedding is { Count: > 0 })
 247            {
 248                _resolvedEmbeddings[definition.Name] = definition.ReferenceEmbedding;
 249                continue;
 50            }
 51
 952            if (definition.ExampleBehaviorKeys.Count == 0)
 153                continue;
 54
 855            var embeddings = new List<IReadOnlyList<double>>();
 4056            foreach (var key in definition.ExampleBehaviorKeys)
 857            {
 858                var result = await provider.EmbedAsync(key, cancellationToken);
 859                if (result.Vector is { Count: > 0 })
 860                    embeddings.Add(result.Vector);
 861            }
 62
 863            if (embeddings.Count > 0)
 864                _resolvedEmbeddings[definition.Name] = AverageVectors(embeddings);
 865        }
 866    }
 67
 68    /// <summary>
 69    /// Gets the resolved embedding for a definition by name, or null if not resolved.
 70    /// </summary>
 71    public IReadOnlyList<double>? GetEmbedding(string intentName)
 772        => _resolvedEmbeddings.TryGetValue(intentName, out var embedding) ? embedding : null;
 73
 74    /// <summary>
 75    /// Finds the best matching intent by comparing a behavior embedding against all catalog entries.
 76    /// Returns the best match name and cosine similarity score, or null if no match found.
 77    /// </summary>
 78    public (string Name, double Score)? FindBestMatch(IReadOnlyList<double> behaviorEmbedding)
 679    {
 680        if (behaviorEmbedding.Count == 0 || _resolvedEmbeddings.Count == 0)
 281            return null;
 82
 483        string? bestName = null;
 484        var bestScore = -1.0;
 85
 2486        foreach (var (name, refEmbedding) in _resolvedEmbeddings)
 687        {
 688            var score = EmbeddingScore.CosineSimilarity(behaviorEmbedding, refEmbedding);
 689            if (score > bestScore)
 490            {
 491                bestScore = score;
 492                bestName = name;
 493            }
 694        }
 95
 496        return bestName != null ? (bestName, bestScore) : null;
 697    }
 98
 99    private static double[] AverageVectors(List<IReadOnlyList<double>> vectors)
 8100    {
 8101        var dim = vectors[0].Count;
 8102        var avg = new double[dim];
 103
 40104        foreach (var vec in vectors)
 8105        {
 144106            for (var i = 0; i < Math.Min(dim, vec.Count); i++)
 64107                avg[i] += vec[i];
 8108        }
 109
 144110        for (var i = 0; i < dim; i++)
 64111            avg[i] /= vectors.Count;
 112
 8113        return avg;
 8114    }
 115}