| | | 1 | | using Intentum.AI.Embeddings; |
| | | 2 | | using Intentum.AI.Similarity; |
| | | 3 | | using Intentum.Core.Behavior; |
| | | 4 | | using Intentum.Core.Intents; |
| | | 5 | | using Intentum.Core.Pipeline; |
| | | 6 | | |
| | | 7 | | namespace Intentum.AI.Models; |
| | | 8 | | |
| | | 9 | | /// <summary> |
| | | 10 | | /// LLM-based inference step: embeddings + similarity -> raw result (name, score, signals, reasoning). |
| | | 11 | | /// Used by IntentResolutionPipeline; also used by LlmIntentModel internally. |
| | | 12 | | /// </summary> |
| | | 13 | | public sealed class LlmInferenceStep : IIntentInferenceStep |
| | | 14 | | { |
| | | 15 | | private readonly IIntentEmbeddingProvider _embeddingProvider; |
| | | 16 | | private readonly IIntentSimilarityEngine _similarityEngine; |
| | | 17 | | |
| | | 18 | | /// <summary> |
| | | 19 | | /// Creates an LLM inference step with the given embedding provider and similarity engine. |
| | | 20 | | /// </summary> |
| | 73 | 21 | | public LlmInferenceStep( |
| | 73 | 22 | | IIntentEmbeddingProvider embeddingProvider, |
| | 73 | 23 | | IIntentSimilarityEngine similarityEngine) |
| | 73 | 24 | | { |
| | 73 | 25 | | _embeddingProvider = embeddingProvider ?? throw new ArgumentNullException(nameof(embeddingProvider)); |
| | 73 | 26 | | _similarityEngine = similarityEngine ?? throw new ArgumentNullException(nameof(similarityEngine)); |
| | 73 | 27 | | } |
| | | 28 | | |
| | | 29 | | /// <inheritdoc /> |
| | | 30 | | public IntentInferenceResult Infer(BehaviorSpace behaviorSpace, BehaviorVector vector) |
| | 97 | 31 | | { |
| | 97 | 32 | | var embeddings = vector.Dimensions.Keys |
| | 97 | 33 | | .Select(_embeddingProvider.Embed) |
| | 97 | 34 | | .ToList(); |
| | | 35 | | |
| | 97 | 36 | | return BuildResult(behaviorSpace, embeddings, vector); |
| | 97 | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <summary> |
| | | 40 | | /// Async version of inference that properly awaits embedding calls. |
| | | 41 | | /// </summary> |
| | | 42 | | public async Task<IntentInferenceResult> InferAsync( |
| | | 43 | | BehaviorSpace behaviorSpace, |
| | | 44 | | BehaviorVector vector, |
| | | 45 | | CancellationToken cancellationToken = default) |
| | 0 | 46 | | { |
| | 0 | 47 | | var tasks = vector.Dimensions.Keys |
| | 0 | 48 | | .Select(k => _embeddingProvider.EmbedAsync(k, cancellationToken)); |
| | 0 | 49 | | var embeddings = (await Task.WhenAll(tasks)).ToList(); |
| | | 50 | | |
| | 0 | 51 | | return BuildResult(behaviorSpace, embeddings, vector); |
| | 0 | 52 | | } |
| | | 53 | | |
| | | 54 | | private IntentInferenceResult BuildResult( |
| | | 55 | | BehaviorSpace behaviorSpace, |
| | | 56 | | List<IntentEmbedding> embeddings, |
| | | 57 | | BehaviorVector vector) |
| | 97 | 58 | | { |
| | | 59 | | double score; |
| | 97 | 60 | | if (_similarityEngine is ITimeAwareSimilarityEngine timeAware) |
| | 0 | 61 | | score = timeAware.CalculateIntentScoreWithTimeDecay(behaviorSpace, embeddings); |
| | | 62 | | else |
| | 97 | 63 | | score = _similarityEngine.CalculateIntentScore(embeddings, vector.Dimensions); |
| | | 64 | | |
| | 97 | 65 | | var signals = embeddings.Select(e => |
| | 228 | 66 | | new IntentSignal( |
| | 228 | 67 | | Source: "ai", |
| | 228 | 68 | | Description: e.Source, |
| | 228 | 69 | | Weight: e.Score)) |
| | 97 | 70 | | .ToList(); |
| | | 71 | | |
| | 97 | 72 | | return new IntentInferenceResult( |
| | 97 | 73 | | Name: "AI-Inferred-Intent", |
| | 97 | 74 | | Score: score, |
| | 97 | 75 | | Signals: signals, |
| | 97 | 76 | | Reasoning: null |
| | 97 | 77 | | ); |
| | 97 | 78 | | } |
| | | 79 | | } |