< Summary

Information
Class: OpenAiIntegration.OpenAiPredictor
Assembly: OpenAiIntegration
File(s): /home/runner/work/KicktippAi/KicktippAi/src/OpenAiIntegration/OpenAiPredictor.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 64
Coverable lines: 64
Total lines: 127
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 22
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
PredictAsync()100%210%
GeneratePrompt(...)100%210%
ParsePrediction(...)0%506220%

File(s)

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

#LineLine coverage
 1using System.ClientModel;
 2using EHonda.KicktippAi.Core;
 3using Microsoft.Extensions.Logging;
 4using OpenAI.Chat;
 5
 6namespace OpenAiIntegration;
 7
 8public class OpenAiPredictor : IPredictor<PredictorContext>
 9{
 10    private readonly ChatClient _client;
 11    private readonly ILogger<OpenAiPredictor> _logger;
 12
 013    public OpenAiPredictor(ChatClient client, ILogger<OpenAiPredictor> logger)
 14    {
 015        _client = client;
 016        _logger = logger;
 017    }
 18
 19    public async Task<Prediction> PredictAsync(Match match, PredictorContext context, CancellationToken cancellationToke
 20    {
 021        _logger.LogInformation("Generating prediction for match: {HomeTeam} vs {AwayTeam} at {StartTime}",
 022            match.HomeTeam, match.AwayTeam, match.StartsAt);
 23
 24        try
 25        {
 026            var prompt = GeneratePrompt(match, context);
 027            _logger.LogDebug("Generated prompt: {Prompt}", prompt);
 28
 029            var messages = new List<ChatMessage>
 030            {
 031                new UserChatMessage(prompt)
 032            };
 33
 034            var response = await _client.CompleteChatAsync(messages, cancellationToken: cancellationToken);
 035            _logger.LogDebug("Received response from OpenAI");
 36
 037            var prediction = ParsePrediction(response);
 038            _logger.LogInformation("Prediction generated: {HomeGoals}-{AwayGoals} for {HomeTeam} vs {AwayTeam}",
 039                prediction.HomeGoals, prediction.AwayGoals, match.HomeTeam, match.AwayTeam);
 40
 041            return prediction;
 42        }
 043        catch (Exception ex)
 44        {
 045            _logger.LogError(ex, "Error generating prediction for match: {HomeTeam} vs {AwayTeam}",
 046                match.HomeTeam, match.AwayTeam);
 47
 48            // Return a fallback prediction in case of error
 049            _logger.LogWarning("Returning fallback prediction (1-1) due to error");
 050            return new Prediction(1, 1);
 51        }
 052    }
 53
 54    private string GeneratePrompt(Match match, PredictorContext context)
 55    {
 056        var prompt = $@"You are a football prediction expert. Predict the final score for this match:
 057
 058Match: {match.HomeTeam} vs {match.AwayTeam}
 059Kick-off: {match.StartsAt:yyyy-MM-dd HH:mm}
 060
 061Please provide your prediction in the following format only:
 062HOME_GOALS-AWAY_GOALS
 063
 064For example: 2-1
 065
 066Consider:
 067- Home advantage (home teams typically score slightly more)
 068- Recent form and performance
 069- Common football scores (0-0, 1-0, 1-1, 2-0, 2-1, etc.)
 070
 071Your prediction:";
 72
 073        return prompt;
 74    }
 75
 76    private Prediction ParsePrediction(ClientResult<ChatCompletion>? response)
 77    {
 78        try
 79        {
 080            if (response?.Value?.Content == null || !response.Value.Content.Any())
 81            {
 082                _logger.LogWarning("No content in OpenAI response, using fallback prediction");
 083                return new Prediction(1, 1);
 84            }
 85
 086            var content = response.Value.Content[0].Text?.Trim();
 087            if (string.IsNullOrEmpty(content))
 88            {
 089                _logger.LogWarning("Empty content in OpenAI response, using fallback prediction");
 090                return new Prediction(1, 1);
 91            }
 92
 093            _logger.LogDebug("Parsing response content: {Content}", content);
 94
 95            // Look for pattern like "2-1" in the response
 096            var scorePattern = System.Text.RegularExpressions.Regex.Match(content, @"(\d+)-(\d+)");
 97
 098            if (scorePattern.Success)
 99            {
 0100                var homeGoals = int.Parse(scorePattern.Groups[1].Value);
 0101                var awayGoals = int.Parse(scorePattern.Groups[2].Value);
 102
 103                // Validate reasonable score range (0-10 goals per team)
 0104                if (homeGoals >= 0 && homeGoals <= 10 && awayGoals >= 0 && awayGoals <= 10)
 105                {
 0106                    return new Prediction(homeGoals, awayGoals);
 107                }
 108                else
 109                {
 0110                    _logger.LogWarning("Parsed scores out of reasonable range: {HomeGoals}-{AwayGoals}, using fallback",
 0111                        homeGoals, awayGoals);
 0112                    return new Prediction(1, 1);
 113                }
 114            }
 115            else
 116            {
 0117                _logger.LogWarning("Could not parse score from response: {Content}, using fallback prediction", content)
 0118                return new Prediction(1, 1);
 119            }
 120        }
 0121        catch (Exception ex)
 122        {
 0123            _logger.LogError(ex, "Error parsing prediction response, using fallback prediction");
 0124            return new Prediction(1, 1);
 125        }
 0126    }
 127}