< Summary

Information
Class: Intentum.AI.Caching.CachedIntentModel
Assembly: Intentum.AI
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.AI/Caching/CachedIntentModel.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 31
Coverable lines: 31
Total lines: 68
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 12
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
.ctor(...)0%440%
Infer(...)0%660%
VectorToCacheKey(...)0%220%

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.AI/Caching/CachedIntentModel.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2using System.Text;
 3using System.Text.Json;
 4using Intentum.Core.Behavior;
 5using Intentum.Core.Caching;
 6using Intentum.Core.Contracts;
 7using Intentum.Core.Intents;
 8
 9namespace Intentum.AI.Caching;
 10
 11/// <summary>
 12/// Wraps an intent model and caches results by behavior vector hash (exact match).
 13/// Use to skip recomputation when the same or identical vector was inferred recently.
 14/// For similarity-threshold matching (e.g. "close enough" vector), implement a custom cache key strategy.
 15/// </summary>
 16public sealed class CachedIntentModel : IIntentModel
 17{
 018    private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCas
 19    private readonly IIntentModel _inner;
 20    private readonly IIntentResultCache _cache;
 21    private readonly TimeSpan? _ttl;
 22
 23    /// <summary>
 24    /// Creates a cached intent model.
 25    /// </summary>
 26    /// <param name="inner">The inner intent model.</param>
 27    /// <param name="cache">Cache for intent results (e.g. MemoryIntentResultCache or Redis-backed).</param>
 28    /// <param name="ttl">Optional time-to-live for cached entries.</param>
 029    public CachedIntentModel(IIntentModel inner, IIntentResultCache cache, TimeSpan? ttl = null)
 030    {
 031        _inner = inner ?? throw new ArgumentNullException(nameof(inner));
 032        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
 033        _ttl = ttl;
 034    }
 35
 36    /// <inheritdoc />
 37    public Intent Infer(BehaviorSpace behaviorSpace, BehaviorVector? precomputedVector = null)
 038    {
 039        var vector = precomputedVector ?? behaviorSpace.ToVector();
 040        var key = VectorToCacheKey(vector);
 041        var (found, value) = _cache.TryGetAsync(key).GetAwaiter().GetResult();
 042        if (found && value != null)
 043        {
 44            try
 045            {
 046                return JsonSerializer.Deserialize<Intent>(value, JsonOptions)!;
 47            }
 048            catch
 049            {
 50                // Invalid cache entry; fall through to infer
 051            }
 052        }
 53
 054        var intent = _inner.Infer(behaviorSpace, vector);
 055        var json = JsonSerializer.Serialize(intent, JsonOptions);
 056        _cache.SetAsync(key, json, _ttl).GetAwaiter().GetResult();
 057        return intent;
 058    }
 59
 60    private static string VectorToCacheKey(BehaviorVector vector)
 061    {
 062        var sb = new StringBuilder();
 063        foreach (var kv in vector.Dimensions.OrderBy(k => k.Key, StringComparer.Ordinal))
 064            sb.Append(kv.Key).Append(':').Append(kv.Value).Append(';');
 065        var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(sb.ToString()));
 066        return Convert.ToHexString(bytes);
 067    }
 68}