< Summary

Information
Class: OpenAiIntegration.InstructionsTemplateProvider
Assembly: OpenAiIntegration
File(s): /home/runner/work/KicktippAi/KicktippAi/src/OpenAiIntegration/InstructionsTemplateProvider.cs
Line coverage
93%
Covered lines: 40
Uncovered lines: 3
Coverable lines: 43
Total lines: 98
Line coverage: 93%
Branch coverage
95%
Covered branches: 21
Total branches: 22
Branch coverage: 95.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
LoadMatchTemplate(...)100%88100%
LoadBonusTemplate(...)100%22100%
ReadFileContent(...)50%2262.5%
GetPromptModelForModel(...)100%1010100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/OpenAiIntegration/InstructionsTemplateProvider.cs

#LineLine coverage
 1using Microsoft.Extensions.FileProviders;
 2
 3namespace OpenAiIntegration;
 4
 5/// <summary>
 6/// Default implementation that loads instructions templates from the file system
 7/// </summary>
 8public class InstructionsTemplateProvider : IInstructionsTemplateProvider
 9{
 10    private readonly IFileProvider _fileProvider;
 11
 112    public InstructionsTemplateProvider(IFileProvider fileProvider)
 13    {
 114        _fileProvider = fileProvider;
 115    }
 16
 17    public (string template, string path) LoadMatchTemplate(string model, bool includeJustification)
 18    {
 119        var promptModel = GetPromptModelForModel(model);
 120        var fileName = includeJustification ? "match.justification.md" : "match.md";
 121        var filePath = $"{promptModel}/{fileName}";
 22
 123        var fileInfo = _fileProvider.GetFileInfo(filePath);
 124        if (fileInfo.Exists)
 25        {
 126            return ReadFileContent(fileInfo);
 27        }
 28
 129        if (includeJustification)
 30        {
 131            var fallbackPath = $"{promptModel}/match.md";
 132            var fallbackFileInfo = _fileProvider.GetFileInfo(fallbackPath);
 133            if (fallbackFileInfo.Exists)
 34            {
 135                return ReadFileContent(fallbackFileInfo);
 36            }
 37        }
 38
 139        throw new FileNotFoundException($"Match instructions not found at: {filePath}");
 40    }
 41
 42    public (string template, string path) LoadBonusTemplate(string model)
 43    {
 144        var promptModel = GetPromptModelForModel(model);
 145        var filePath = $"{promptModel}/bonus.md";
 46
 147        var fileInfo = _fileProvider.GetFileInfo(filePath);
 148        if (fileInfo.Exists)
 49        {
 150            return ReadFileContent(fileInfo);
 51        }
 52
 153        throw new FileNotFoundException($"Bonus instructions not found at: {filePath}");
 54    }
 55
 56    /// <summary>
 57    /// Reads the content from a file info and returns it with the physical path
 58    /// </summary>
 59    /// <param name="fileInfo">The file info to read from</param>
 60    /// <returns>A tuple containing the file content and physical path</returns>
 61    /// <exception cref="InvalidOperationException">Thrown when the physical path is null</exception>
 62    private static (string content, string path) ReadFileContent(IFileInfo fileInfo)
 63    {
 164        if (fileInfo.PhysicalPath == null)
 65        {
 066            throw new InvalidOperationException(
 067                $"File '{fileInfo.Name}' does not have a physical path. " +
 068                "This may indicate the file is from a non-physical file provider.");
 69        }
 70
 171        using var stream = fileInfo.CreateReadStream();
 172        using var reader = new StreamReader(stream);
 173        return (reader.ReadToEnd(), fileInfo.PhysicalPath);
 174    }
 75
 76    /// <summary>
 77    /// Maps a model name to the appropriate prompt directory, handling cross-model mappings
 78    /// </summary>
 79    /// <param name="model">The model name to map</param>
 80    /// <returns>The prompt directory name to use</returns>
 81    private static string GetPromptModelForModel(string model)
 82    {
 183        return model switch
 184        {
 185            // Direct mappings
 186            "o3" => "o3",
 187            "gpt-5" => "gpt-5",
 188
 189            // Cross-model mappings
 190            "o4-mini" => "o3",
 191            "gpt-5-mini" => "gpt-5",
 192            "gpt-5-nano" => "gpt-5",
 193
 194            // Default to the model name itself for any new models
 195            _ => model
 196        };
 97    }
 98}