< Summary

Information
Class: Orchestrator.Infrastructure.Langfuse.LangfuseTextPromptTemplateProvider.ResolvedPrompt
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/Langfuse/LangfuseTextPromptTemplateProvider.cs
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 142
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

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/Langfuse/LangfuseTextPromptTemplateProvider.cs

#LineLine coverage
 1using OpenAiIntegration;
 2
 3namespace Orchestrator.Infrastructure.Langfuse;
 4
 5internal enum LangfusePromptKind
 6{
 7    Match,
 8    Bonus
 9}
 10
 11internal sealed class LangfuseTextPromptTemplateProvider : IInstructionsTemplateProvider, IPromptTemplateTelemetryMetada
 12{
 13    private readonly ILangfusePublicApiClient _client;
 14    private readonly string _promptName;
 15    private readonly string? _label;
 16    private readonly int? _version;
 17    private readonly LangfusePrompt? _preloadedPrompt;
 18    private readonly LangfusePromptKind _promptKind;
 19    private readonly IInstructionsTemplateProvider? _fallbackTemplateProvider;
 20    private readonly string? _fallbackModel;
 21    private readonly Action<string>? _fallbackWarning;
 22    private readonly Lazy<ResolvedPrompt> _prompt;
 23
 24    public LangfuseTextPromptTemplateProvider(
 25        ILangfusePublicApiClient client,
 26        string promptName,
 27        string? label,
 28        int? version,
 29        LangfusePrompt? preloadedPrompt = null,
 30        LangfusePromptKind promptKind = LangfusePromptKind.Match,
 31        IInstructionsTemplateProvider? fallbackTemplateProvider = null,
 32        string? fallbackModel = null,
 33        Action<string>? fallbackWarning = null)
 34    {
 35        _client = client ?? throw new ArgumentNullException(nameof(client));
 36        _promptName = string.IsNullOrWhiteSpace(promptName)
 37            ? throw new ArgumentException("Langfuse prompt name must be provided.", nameof(promptName))
 38            : promptName.Trim();
 39        _label = string.IsNullOrWhiteSpace(label) ? null : label.Trim();
 40        _version = version;
 41        _preloadedPrompt = preloadedPrompt;
 42        _promptKind = promptKind;
 43        _fallbackTemplateProvider = fallbackTemplateProvider;
 44        _fallbackModel = string.IsNullOrWhiteSpace(fallbackModel) ? null : fallbackModel.Trim();
 45        _fallbackWarning = fallbackWarning;
 46        _prompt = new Lazy<ResolvedPrompt>(LoadPrompt);
 47    }
 48
 49    public LangfusePrompt? Prompt => _prompt.Value.Prompt;
 50
 51    public PromptTemplateTelemetryMetadata? GetPromptTemplateTelemetryMetadata()
 52    {
 53        return _prompt.IsValueCreated ? _prompt.Value.TelemetryMetadata : null;
 54    }
 55
 56    public (string template, string path) LoadMatchTemplate(string model, bool includeJustification)
 57    {
 58        if (_promptKind != LangfusePromptKind.Match)
 59        {
 60            throw new NotSupportedException("This Langfuse prompt provider is configured for bonus prompts.");
 61        }
 62
 63        if (includeJustification)
 64        {
 65            throw new NotSupportedException(
 66                "The Langfuse prompt source only supports WM 2026 match prompts without justification in this version.")
 67        }
 68
 69        var prompt = _prompt.Value;
 70        return (prompt.Template, prompt.Path);
 71    }
 72
 73    public (string template, string path) LoadBonusTemplate(string model)
 74    {
 75        if (_promptKind != LangfusePromptKind.Bonus)
 76        {
 77            throw new NotSupportedException("This Langfuse prompt provider is configured for match prompts.");
 78        }
 79
 80        var prompt = _prompt.Value;
 81        return (prompt.Template, prompt.Path);
 82    }
 83
 84    private ResolvedPrompt LoadPrompt()
 85    {
 86        try
 87        {
 88            var prompt = _preloadedPrompt
 89                         ?? _client.GetPromptAsync(_promptName, _label, _version)
 90                             .GetAwaiter()
 91                             .GetResult();
 92
 93            if (prompt is not null)
 94            {
 95                var path = BuildPromptPath(prompt);
 96                return new ResolvedPrompt(
 97                    prompt.GetTextPrompt(),
 98                    path,
 99                    prompt,
 100                    new PromptTemplateTelemetryMetadata(prompt.Name, prompt.Version, IsFallback: false, path));
 101            }
 102
 103            return LoadFallbackPrompt($"Langfuse prompt '{_promptName}' was not found.");
 104        }
 105        catch (Exception ex) when (_fallbackTemplateProvider is not null)
 106        {
 107            return LoadFallbackPrompt($"Failed to fetch Langfuse prompt '{_promptName}': {ex.Message}");
 108        }
 109    }
 110
 111    private string BuildPromptPath(LangfusePrompt prompt)
 112    {
 113        var labelSuffix = string.IsNullOrWhiteSpace(_label) ? string.Empty : $"?label={Uri.EscapeDataString(_label)}";
 114        return $"langfuse://prompts/{Uri.EscapeDataString(prompt.Name)}/versions/{prompt.Version}{labelSuffix}";
 115    }
 116
 117    private ResolvedPrompt LoadFallbackPrompt(string reason)
 118    {
 119        if (_fallbackTemplateProvider is null || string.IsNullOrWhiteSpace(_fallbackModel))
 120        {
 121            throw new FileNotFoundException(
 122                $"{reason} No local fallback prompt was configured for '{_promptName}'.");
 123        }
 124
 125        var fallback = _promptKind == LangfusePromptKind.Match
 126            ? _fallbackTemplateProvider.LoadMatchTemplate(_fallbackModel, includeJustification: false)
 127            : _fallbackTemplateProvider.LoadBonusTemplate(_fallbackModel);
 128
 129        _fallbackWarning?.Invoke($"{reason} Using local fallback prompt '{fallback.path}'.");
 130        return new ResolvedPrompt(
 131            fallback.template,
 132            fallback.path,
 133            Prompt: null,
 134            new PromptTemplateTelemetryMetadata(_promptName, null, IsFallback: true, fallback.path));
 135    }
 136
 1137    private sealed record ResolvedPrompt(
 1138        string Template,
 1139        string Path,
 1140        LangfusePrompt? Prompt,
 1141        PromptTemplateTelemetryMetadata TelemetryMetadata);
 142}