< Summary

Information
Class: Orchestrator.Commands.Observability.ReconstructPrompt.ReconstructPromptCommand
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/ReconstructPrompt/ReconstructPromptCommand.cs
Line coverage
91%
Covered lines: 68
Uncovered lines: 6
Coverable lines: 74
Total lines: 138
Line coverage: 91.8%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
ExecuteAsync()90%101088.89%
ResolveMatchAsync()100%44100%
RehydrateForPromptOutput(...)100%11100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/ReconstructPrompt/ReconstructPromptCommand.cs

#LineLine coverage
 1using EHonda.KicktippAi.Core;
 2using Microsoft.Extensions.Logging;
 3using NodaTime;
 4using OpenAiIntegration;
 5using Orchestrator.Commands.Observability;
 6using Orchestrator.Infrastructure.Factories;
 7using Spectre.Console;
 8using Spectre.Console.Cli;
 9
 10namespace Orchestrator.Commands.Observability.ReconstructPrompt;
 11
 12/// <summary>
 13/// Reconstructs historical prompt inputs for a stored match prediction.
 14/// </summary>
 15public class ReconstructPromptCommand : AsyncCommand<ReconstructPromptSettings>
 16{
 117    private static readonly DateTimeZone BundesligaTimeZone = DateTimeZoneProviders.Tzdb["Europe/Berlin"];
 18
 19    private readonly IAnsiConsole _console;
 20    private readonly IFirebaseServiceFactory _firebaseServiceFactory;
 21    private readonly ILogger<ReconstructPromptCommand> _logger;
 22
 123    public ReconstructPromptCommand(
 124        IAnsiConsole console,
 125        IFirebaseServiceFactory firebaseServiceFactory,
 126        ILogger<ReconstructPromptCommand> logger)
 27    {
 128        _console = console;
 129        _firebaseServiceFactory = firebaseServiceFactory;
 130        _logger = logger;
 131    }
 32
 33    public override async Task<int> ExecuteAsync(CommandContext context, ReconstructPromptSettings settings)
 34    {
 35        try
 36        {
 137            _console.MarkupLine($"[green]Reconstructing prompt for:[/] [yellow]{Markup.Escape(settings.HomeTeam)}[/] vs 
 38
 139            var predictionRepository = _firebaseServiceFactory.CreatePredictionRepository();
 140            var contextRepository = _firebaseServiceFactory.CreateContextRepository();
 141            var reconstructionService = new MatchPromptReconstructionService(
 142                predictionRepository,
 143                contextRepository,
 144                new InstructionsTemplateProvider(PromptsFileProvider.Create()));
 45
 146            var evaluationTime = EvaluationTimeParser.ParseOrNull(settings.EvaluationTime);
 47
 148            var match = await ResolveMatchAsync(predictionRepository, settings, evaluationTime is not null);
 149            if (match is null)
 50            {
 151                _console.MarkupLine($"[red]Match not found on matchday {settings.Matchday}:[/] {Markup.Escape(settings.H
 152                return 1;
 53            }
 54
 155            match = RehydrateForPromptOutput(match);
 56
 57            ReconstructedMatchPredictionPrompt? reconstructedPrompt;
 158            if (evaluationTime is null)
 59            {
 160                reconstructedPrompt = await reconstructionService.ReconstructMatchPredictionPromptAsync(
 161                    match,
 162                    settings.Model,
 163                    settings.CommunityContext,
 164                    settings.WithJustification);
 65            }
 66            else
 67            {
 168                var selection = MatchContextDocumentCatalog.ForMatch(
 169                    match.HomeTeam,
 170                    match.AwayTeam,
 171                    settings.CommunityContext);
 72
 173                reconstructedPrompt = await reconstructionService.ReconstructMatchPredictionPromptAtTimestampAsync(
 174                    match,
 175                    settings.Model,
 176                    settings.CommunityContext,
 177                    evaluationTime.Value,
 178                    selection.RequiredDocumentNames,
 179                    selection.OptionalDocumentNames,
 180                    settings.WithJustification);
 81            }
 82
 183            if (reconstructedPrompt is null)
 84            {
 085                _console.MarkupLine("[red]No stored prediction metadata found for that match/model/community combination
 086                return 1;
 87            }
 88
 189            var timestampLabel = evaluationTime is null ? "Prediction timestamp" : "Reconstruction timestamp";
 190            _console.MarkupLine($"[blue]{timestampLabel}:[/] {reconstructedPrompt.PromptTimestamp:O}");
 191            _console.MarkupLine($"[blue]Prompt template:[/] {Markup.Escape(reconstructedPrompt.PromptTemplatePath)}");
 192            _console.MarkupLine($"[blue]Justification variant:[/] {reconstructedPrompt.IncludeJustification}");
 193            _console.WriteLine();
 194            _console.MarkupLine("[green]Resolved context versions:[/]");
 95
 196            foreach (var document in reconstructedPrompt.ResolvedContextDocuments)
 97            {
 198                _console.MarkupLine($"[dim]- {Markup.Escape(document.DocumentName)} | v{document.Version} | {document.Cr
 99            }
 100
 1101            _console.WriteLine();
 1102            _console.MarkupLine("[green]Match JSON:[/]");
 1103            _console.WriteLine(reconstructedPrompt.MatchJson);
 1104            _console.WriteLine();
 1105            _console.MarkupLine("[green]System prompt:[/]");
 1106            _console.WriteLine(reconstructedPrompt.SystemPrompt);
 107
 1108            return 0;
 109        }
 0110        catch (Exception ex)
 111        {
 0112            _logger.LogError(ex, "Error executing reconstruct-prompt command");
 0113            _console.MarkupLine($"[red]Error:[/] {Markup.Escape(ex.Message)}");
 0114            return 1;
 115        }
 1116    }
 117
 118    private static async Task<Match?> ResolveMatchAsync(
 119        IPredictionRepository predictionRepository,
 120        ReconstructPromptSettings settings,
 121        bool allowExactTimestampFallback)
 122    {
 1123        return await predictionRepository.GetStoredMatchAsync(
 1124            settings.HomeTeam,
 1125            settings.AwayTeam,
 1126            settings.Matchday!.Value,
 1127            allowExactTimestampFallback ? null : settings.Model,
 1128            allowExactTimestampFallback ? null : settings.CommunityContext);
 1129    }
 130
 131    private static Match RehydrateForPromptOutput(Match match)
 132    {
 1133        var instant = match.StartsAt.ToInstant();
 1134        var offset = BundesligaTimeZone.GetUtcOffset(instant);
 1135        var localizedStartsAt = instant.InZone(DateTimeZone.ForOffset(offset));
 1136        return new Match(match.HomeTeam, match.AwayTeam, localizedStartsAt, match.Matchday, match.IsCancelled);
 137    }
 138}