| | | 1 | | using System.Text; |
| | | 2 | | using System.Text.Json; |
| | | 3 | | using EHonda.KicktippAi.Core; |
| | | 4 | | |
| | | 5 | | namespace OpenAiIntegration; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Shared helpers for building prompt inputs used by prediction and reconstruction flows. |
| | | 9 | | /// </summary> |
| | | 10 | | public static class PredictionPromptComposer |
| | | 11 | | { |
| | | 12 | | private const string ContextDocumentsPlaceholder = "{{context_documents}}"; |
| | | 13 | | |
| | | 14 | | public static string BuildSystemPrompt(string template, IEnumerable<DocumentContext> contextDocuments) |
| | | 15 | | { |
| | 1 | 16 | | var contextList = contextDocuments.ToList(); |
| | 1 | 17 | | if (template.Contains(ContextDocumentsPlaceholder, StringComparison.Ordinal)) |
| | | 18 | | { |
| | 1 | 19 | | return template.Replace( |
| | 1 | 20 | | ContextDocumentsPlaceholder, |
| | 1 | 21 | | BuildContextDocumentsSection(contextList, includeLeadingNewLine: false), |
| | 1 | 22 | | StringComparison.Ordinal); |
| | | 23 | | } |
| | | 24 | | |
| | 1 | 25 | | if (contextList.Count == 0) |
| | | 26 | | { |
| | 1 | 27 | | return template; |
| | | 28 | | } |
| | | 29 | | |
| | 1 | 30 | | return template + BuildContextDocumentsSection(contextList, includeLeadingNewLine: true); |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | private static string BuildContextDocumentsSection( |
| | | 34 | | IReadOnlyList<DocumentContext> contextDocuments, |
| | | 35 | | bool includeLeadingNewLine) |
| | | 36 | | { |
| | 1 | 37 | | if (contextDocuments.Count == 0) |
| | | 38 | | { |
| | 1 | 39 | | return string.Empty; |
| | | 40 | | } |
| | | 41 | | |
| | 1 | 42 | | var contextSection = new StringBuilder(); |
| | 1 | 43 | | if (includeLeadingNewLine) |
| | | 44 | | { |
| | 1 | 45 | | contextSection.Append('\n'); |
| | | 46 | | } |
| | | 47 | | |
| | 1 | 48 | | foreach (var doc in contextDocuments) |
| | | 49 | | { |
| | 1 | 50 | | contextSection.Append("---\n"); |
| | 1 | 51 | | contextSection.Append(doc.Name); |
| | 1 | 52 | | contextSection.Append("\n\n"); |
| | 1 | 53 | | contextSection.Append(doc.Content); |
| | 1 | 54 | | contextSection.Append('\n'); |
| | | 55 | | } |
| | | 56 | | |
| | 1 | 57 | | contextSection.Append("---"); |
| | 1 | 58 | | return contextSection.ToString(); |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | public static string CreateMatchJson(Match match) |
| | | 62 | | { |
| | 1 | 63 | | return JsonSerializer.Serialize(new |
| | 1 | 64 | | { |
| | 1 | 65 | | homeTeam = match.HomeTeam, |
| | 1 | 66 | | awayTeam = match.AwayTeam, |
| | 1 | 67 | | startsAt = match.StartsAt.ToString() |
| | 1 | 68 | | }, new JsonSerializerOptions |
| | 1 | 69 | | { |
| | 1 | 70 | | Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping |
| | 1 | 71 | | }); |
| | | 72 | | } |
| | | 73 | | |
| | | 74 | | public static string CreateBonusQuestionJson(BonusQuestion question) |
| | | 75 | | { |
| | 1 | 76 | | var questionData = new |
| | 1 | 77 | | { |
| | 1 | 78 | | text = question.Text, |
| | 1 | 79 | | options = question.Options.Select(o => new { id = o.Id, text = o.Text }).ToArray(), |
| | 1 | 80 | | maxSelections = question.MaxSelections |
| | 1 | 81 | | }; |
| | | 82 | | |
| | 1 | 83 | | return JsonSerializer.Serialize(questionData, new JsonSerializerOptions |
| | 1 | 84 | | { |
| | 1 | 85 | | Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping |
| | 1 | 86 | | }); |
| | | 87 | | } |
| | | 88 | | } |