< Summary

Information
Class: Intentum.AI.Classification.PromptClassifierOptions
Assembly: Intentum.AI
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.AI/Classification/PromptIntentClassifier.cs
Line coverage
100%
Covered lines: 3
Uncovered lines: 0
Coverable lines: 3
Total lines: 127
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.AI/Classification/PromptIntentClassifier.cs

#LineLine coverage
 1#pragma warning disable IDE0052 // Positional properties used by JSON serialization
 2using System.Net.Http.Json;
 3using System.Text.Json;
 4using System.Text.Json.Serialization;
 5using Intentum.AI.Http;
 6using Intentum.Core.Behavior;
 7
 8namespace Intentum.AI.Classification;
 9
 10/// <summary>
 11/// LLM prompt-based intent classifier. Sends behavior events to an LLM API
 12/// and receives structured JSON with intent name, confidence, and reasoning.
 13/// Supports OpenAI-compatible chat completion APIs.
 14/// </summary>
 15public sealed class PromptIntentClassifier : IIntentClassifier
 16{
 17    private readonly HttpClient _httpClient;
 18    private readonly PromptClassifierOptions _options;
 19
 20    public PromptIntentClassifier(HttpClient httpClient, PromptClassifierOptions options)
 21    {
 22        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
 23        _options = options ?? throw new ArgumentNullException(nameof(options));
 24    }
 25
 26    public async Task<IntentClassificationResult> ClassifyAsync(
 27        BehaviorSpace behaviorSpace,
 28        IReadOnlyList<string>? candidateIntents = null,
 29        CancellationToken cancellationToken = default)
 30    {
 31        var events = behaviorSpace.Events
 32            .Select(e => $"  - {e.Actor}:{e.Action}")
 33            .ToList();
 34
 35        var candidateList = candidateIntents is { Count: > 0 }
 36            ? $"\nCandidate intents (pick one): {string.Join(", ", candidateIntents)}"
 37            : "";
 38
 39        var systemPrompt = _options.SystemPrompt ?? DefaultSystemPrompt;
 40        var jsonExample = """{"intent":"...","confidence":0.0-1.0,"reasoning":"..."}""";
 41        var userPrompt = $"""
 42            Behavior events observed:
 43            {string.Join("\n", events)}
 44            {candidateList}
 45
 46            Classify the intent. Respond with JSON only: {jsonExample}
 47            """;
 48
 49        var request = new ChatCompletionRequest(
 50            _options.Model,
 51            [
 52                new ChatMessage("system", systemPrompt),
 53                new ChatMessage("user", userPrompt)
 54            ],
 55            new ResponseFormat("json_object"));
 56
 57        var response = await EmbeddingHttpRetryHandler.SendWithRetryAsync(
 58            ct => _httpClient.PostAsJsonAsync(_options.Endpoint ?? "chat/completions", request, ct),
 59            (_, body) => throw new HttpRequestException($"LLM rate limited: {body}"),
 60            cancellationToken: cancellationToken);
 61
 62        var completion = await response.Content
 63            .ReadFromJsonAsync<ChatCompletionResponse>(cancellationToken);
 64
 65        var content = completion?.Choices?.FirstOrDefault()?.Message?.Content;
 66        if (string.IsNullOrWhiteSpace(content))
 67            return new IntentClassificationResult("Unknown", 0, "Empty LLM response");
 68
 69        try
 70        {
 71            var parsed = JsonSerializer.Deserialize<LlmClassificationOutput>(content, LlmJsonOptions);
 72
 73            return new IntentClassificationResult(
 74                parsed?.Intent ?? "Unknown",
 75                Math.Clamp(parsed?.Confidence ?? 0, 0, 1),
 76                parsed?.Reasoning);
 77        }
 78        catch (JsonException)
 79        {
 80            return new IntentClassificationResult("Unknown", 0, $"Failed to parse LLM response: {content}");
 81        }
 82    }
 83
 84    private static readonly JsonSerializerOptions LlmJsonOptions = new() { PropertyNameCaseInsensitive = true };
 85
 86    private const string DefaultSystemPrompt =
 87        "You are an intent classification system. Given a sequence of behavior events (actor:action pairs), " +
 88        "determine the most likely user intent. Return ONLY valid JSON with fields: intent (string), " +
 89        "confidence (number 0-1), reasoning (string).";
 90
 91    private sealed record ChatCompletionRequest(
 92        [property: JsonPropertyName("model")] string Model,
 93        [property: JsonPropertyName("messages")] IReadOnlyList<ChatMessage> Messages,
 94        [property: JsonPropertyName("response_format")] ResponseFormat? ResponseFormat = null);
 95
 96    private sealed record ChatMessage(
 97        [property: JsonPropertyName("role")] string Role,
 98        [property: JsonPropertyName("content")] string Content);
 99
 100    private sealed record ResponseFormat(
 101        [property: JsonPropertyName("type")] string Type);
 102
 103    private sealed record ChatCompletionResponse(
 104        [property: JsonPropertyName("choices")] List<ChatChoice>? Choices);
 105
 106    private sealed record ChatChoice(
 107        [property: JsonPropertyName("message")] ChatChoiceMessage? Message);
 108
 109    private sealed record ChatChoiceMessage(
 110        [property: JsonPropertyName("content")] string? Content);
 111
 112    private sealed record LlmClassificationOutput(
 113        [property: JsonPropertyName("intent")] string? Intent,
 114        [property: JsonPropertyName("confidence")] double? Confidence,
 115        [property: JsonPropertyName("reasoning")] string? Reasoning);
 116}
 117#pragma warning restore IDE0052
 118
 119/// <summary>
 120/// Options for the prompt-based intent classifier.
 121/// </summary>
 122public sealed class PromptClassifierOptions
 123{
 9124    public required string Model { get; init; }
 8125    public string? Endpoint { get; init; }
 4126    public string? SystemPrompt { get; init; }
 127}

Methods/Properties

Model()
Endpoint()
SystemPrompt()