< 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
92%
Covered lines: 69
Uncovered lines: 6
Coverable lines: 75
Total lines: 140
Line coverage: 92%
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%101089.09%
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    protected override async Task<int> ExecuteAsync(CommandContext context, ReconstructPromptSettings settings, Cancella
 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);
 147            var modelConfig = PredictionModelConfig.Create(settings.Model, settings.ReasoningEffort);
 48
 149            var match = await ResolveMatchAsync(predictionRepository, settings, modelConfig, evaluationTime is not null)
 150            if (match is null)
 51            {
 152                _console.MarkupLine($"[red]Match not found on matchday {settings.Matchday}:[/] {Markup.Escape(settings.H
 153                return 1;
 54            }
 55
 156            match = RehydrateForPromptOutput(match);
 57
 58            ReconstructedMatchPredictionPrompt? reconstructedPrompt;
 159            if (evaluationTime is null)
 60            {
 161                reconstructedPrompt = await reconstructionService.ReconstructMatchPredictionPromptAsync(
 162                    match,
 163                    modelConfig,
 164                    settings.CommunityContext,
 165                    settings.WithJustification);
 66            }
 67            else
 68            {
 169                var selection = MatchContextDocumentCatalog.ForMatch(
 170                    match.HomeTeam,
 171                    match.AwayTeam,
 172                    settings.CommunityContext);
 73
 174                reconstructedPrompt = await reconstructionService.ReconstructMatchPredictionPromptAtTimestampAsync(
 175                    match,
 176                    settings.Model,
 177                    settings.CommunityContext,
 178                    evaluationTime.Value,
 179                    selection.RequiredDocumentNames,
 180                    selection.OptionalDocumentNames,
 181                    settings.WithJustification);
 82            }
 83
 184            if (reconstructedPrompt is null)
 85            {
 086                _console.MarkupLine("[red]No stored prediction metadata found for that match/model/community combination
 087                return 1;
 88            }
 89
 190            var timestampLabel = evaluationTime is null ? "Prediction timestamp" : "Reconstruction timestamp";
 191            _console.MarkupLine($"[blue]{timestampLabel}:[/] {reconstructedPrompt.PromptTimestamp:O}");
 192            _console.MarkupLine($"[blue]Prompt template:[/] {Markup.Escape(reconstructedPrompt.PromptTemplatePath)}");
 193            _console.MarkupLine($"[blue]Justification variant:[/] {reconstructedPrompt.IncludeJustification}");
 194            _console.WriteLine();
 195            _console.MarkupLine("[green]Resolved context versions:[/]");
 96
 197            foreach (var document in reconstructedPrompt.ResolvedContextDocuments)
 98            {
 199                _console.MarkupLine($"[dim]- {Markup.Escape(document.DocumentName)} | v{document.Version} | {document.Cr
 100            }
 101
 1102            _console.WriteLine();
 1103            _console.MarkupLine("[green]Match JSON:[/]");
 1104            _console.WriteLine(reconstructedPrompt.MatchJson);
 1105            _console.WriteLine();
 1106            _console.MarkupLine("[green]System prompt:[/]");
 1107            _console.WriteLine(reconstructedPrompt.SystemPrompt);
 108
 1109            return 0;
 110        }
 0111        catch (Exception ex)
 112        {
 0113            _logger.LogError(ex, "Error executing reconstruct-prompt command");
 0114            _console.MarkupLine($"[red]Error:[/] {Markup.Escape(ex.Message)}");
 0115            return 1;
 116        }
 1117    }
 118
 119    private static async Task<Match?> ResolveMatchAsync(
 120        IPredictionRepository predictionRepository,
 121        ReconstructPromptSettings settings,
 122        PredictionModelConfig modelConfig,
 123        bool allowExactTimestampFallback)
 124    {
 1125        return await predictionRepository.GetStoredMatchAsync(
 1126            settings.HomeTeam,
 1127            settings.AwayTeam,
 1128            settings.Matchday!.Value,
 1129            allowExactTimestampFallback ? null : modelConfig,
 1130            allowExactTimestampFallback ? null : settings.CommunityContext);
 1131    }
 132
 133    private static Match RehydrateForPromptOutput(Match match)
 134    {
 1135        var instant = match.StartsAt.ToInstant();
 1136        var offset = BundesligaTimeZone.GetUtcOffset(instant);
 1137        var localizedStartsAt = instant.InZone(DateTimeZone.ForOffset(offset));
 1138        return new Match(match.HomeTeam, match.AwayTeam, localizedStartsAt, match.Matchday, match.IsCancelled);
 139    }
 140}