< Summary

Information
Class: Orchestrator.Commands.Observability.Experiments.PreparedExperimentSupport
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/Experiments/PreparedExperimentSupport.cs
Line coverage
86%
Covered lines: 245
Uncovered lines: 37
Coverable lines: 282
Total lines: 512
Line coverage: 86.8%
Branch coverage
74%
Covered branches: 134
Total branches: 180
Branch coverage: 74.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/Experiments/PreparedExperimentSupport.cs

#LineLine coverage
 1using System.Globalization;
 2using System.Security.Cryptography;
 3using System.Text;
 4using System.Text.Json;
 5using System.Text.Json.Nodes;
 6using EHonda.KicktippAi.Core;
 7
 8namespace Orchestrator.Commands.Observability.Experiments;
 9
 10internal static class PreparedExperimentSupport
 11{
 12    public static IReadOnlyList<IReadOnlyList<T>> CreateBatchChunks<T>(IReadOnlyList<T> items, int batchSize)
 13    {
 114        var chunks = new List<IReadOnlyList<T>>();
 115        for (var index = 0; index < items.Count; index += batchSize)
 16        {
 117            chunks.Add(items.Skip(index).Take(batchSize).ToList());
 18        }
 19
 120        return chunks;
 21    }
 22
 23    public static IReadOnlyList<IReadOnlyList<T>> CreateWarmupThenBatchChunks<T>(IReadOnlyList<T> items, int batchCount)
 24    {
 125        ArgumentNullException.ThrowIfNull(items);
 26
 127        if (batchCount < 1)
 28        {
 029            throw new ArgumentOutOfRangeException(nameof(batchCount), batchCount, "Batch count must be at least 1.");
 30        }
 31
 132        if (items.Count == 0)
 33        {
 034            return [];
 35        }
 36
 137        var chunks = new List<IReadOnlyList<T>>
 138        {
 139            new List<T> { items[0] }
 140        };
 41
 142        if (items.Count == 1)
 43        {
 144            return chunks;
 45        }
 46
 147        var remainingItems = items.Skip(1).ToList();
 148        var actualBatchCount = Math.Min(batchCount, remainingItems.Count);
 149        var baseBatchSize = remainingItems.Count / actualBatchCount;
 150        var remainder = remainingItems.Count % actualBatchCount;
 151        var startIndex = 0;
 52
 153        for (var batchIndex = 0; batchIndex < actualBatchCount; batchIndex += 1)
 54        {
 155            var currentBatchSize = baseBatchSize + (batchIndex < remainder ? 1 : 0);
 156            chunks.Add(remainingItems.Skip(startIndex).Take(currentBatchSize).ToList());
 157            startIndex += currentBatchSize;
 58        }
 59
 160        return chunks;
 61    }
 62
 63    public static ExperimentItemScores CalculateScores(Prediction prediction, int expectedHomeGoals, int expectedAwayGoa
 64    {
 165        var predictedDifference = prediction.HomeGoals - prediction.AwayGoals;
 166        var expectedDifference = expectedHomeGoals - expectedAwayGoals;
 167        var predictedTendency = Math.Sign(predictedDifference);
 168        var expectedTendency = Math.Sign(expectedDifference);
 69
 170        var exactHit = prediction.HomeGoals == expectedHomeGoals
 171            && prediction.AwayGoals == expectedAwayGoals;
 172        var outcomeCorrect = predictedTendency == expectedTendency;
 73
 174        var kicktippPoints = 0;
 175        if (exactHit)
 76        {
 177            kicktippPoints = 4;
 78        }
 179        else if (outcomeCorrect && predictedDifference == expectedDifference && expectedTendency != 0)
 80        {
 181            kicktippPoints = 3;
 82        }
 083        else if (outcomeCorrect)
 84        {
 085            kicktippPoints = 2;
 86        }
 87
 188        return new ExperimentItemScores(kicktippPoints);
 89    }
 90
 91    public static ExperimentAggregateScores SummarizeScores(IReadOnlyList<ExperimentItemScores> scoreEntries)
 92    {
 193        var total = scoreEntries.Sum(entry => entry.KicktippPoints);
 194        var average = scoreEntries.Count == 0 ? 0d : (double)total / scoreEntries.Count;
 195        return new ExperimentAggregateScores(total, average);
 96    }
 97
 98    public static ExperimentAggregateScores SummarizeExecutionScores(
 99        IReadOnlyList<PreparedExperimentExecutionSummary> executionSummaries,
 100        string? taskType)
 101    {
 1102        var total = executionSummaries.Sum(summary => summary.Scores.KicktippPoints);
 1103        if (!string.Equals(taskType, "repeated-match-slice", StringComparison.OrdinalIgnoreCase))
 104        {
 1105            var average = executionSummaries.Count == 0 ? 0d : (double)total / executionSummaries.Count;
 1106            return new ExperimentAggregateScores(total, average);
 107        }
 108
 1109        var indexedSummaries = executionSummaries
 1110            .Select(summary => new
 1111            {
 1112                Summary = summary,
 1113                RepetitionIndex = summary.RepetitionIndex ?? TryParseRepeatedMatchSliceRepetition(summary.DatasetItemId)
 1114            })
 1115            .ToList();
 116
 1117        if (indexedSummaries.Count == 0 || indexedSummaries.Any(summary => summary.RepetitionIndex is null))
 118        {
 0119            throw new InvalidOperationException(
 0120                "Repeated-match-slice scoring requires a repetition index on each execution summary.");
 121        }
 122
 1123        var repetitionTotals = indexedSummaries
 1124            .GroupBy(summary => summary.RepetitionIndex!.Value)
 1125            .Select(group => group.Sum(summary => summary.Summary.Scores.KicktippPoints))
 1126            .ToList();
 127
 1128        return new ExperimentAggregateScores(total, repetitionTotals.Average());
 129    }
 130
 131    public static string DeriveExperimentName(PreparedExperimentRunMetadata runMetadata, string runName)
 132    {
 1133        var parts = new[]
 1134            {
 1135                runMetadata.TaskType,
 1136                runMetadata.CommunityContext,
 1137                runMetadata.SliceKey
 1138            }
 1139            .Where(value => !string.IsNullOrWhiteSpace(value))
 1140            .Select(value => ExperimentArtifactSupport.Slugify(value!))
 1141            .ToArray();
 142
 1143        return parts.Length >= 2
 1144            ? string.Join("__", parts)
 1145            : runName;
 146    }
 147
 148    public static JsonElement BuildLangfuseExperimentMetadata(
 149        PreparedExperimentRunMetadata runMetadata,
 150        string experimentName,
 151        string experimentRunName,
 152        IReadOnlyDictionary<string, string?>? extraFields = null)
 153    {
 1154        var node = JsonSerializer.SerializeToNode(runMetadata, PreparedExperimentCommandSupport.JsonOptions)
 1155                   as JsonObject
 1156                   ?? new JsonObject();
 157
 1158        node["experiment_name"] = experimentName;
 1159        node["experiment_run_name"] = experimentRunName;
 160
 1161        if (extraFields is not null)
 162        {
 1163            foreach (var (key, value) in extraFields)
 164            {
 1165                if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value))
 166                {
 1167                    node[key] = value;
 168                }
 169            }
 170        }
 171
 1172        return JsonSerializer.SerializeToElement(node, PreparedExperimentCommandSupport.JsonOptions);
 173    }
 174
 175    public static string CreateScoreId(string scoreName, params string?[] components)
 176    {
 1177        var joined = string.Join(
 1178            "\n",
 1179            new[] { scoreName }
 1180                .Concat(components.Where(component => !string.IsNullOrWhiteSpace(component)).Select(component => compone
 1181        var hash = SHA256.HashData(Encoding.UTF8.GetBytes(joined));
 1182        return $"exp-score-{Convert.ToHexString(hash).ToLowerInvariant()}";
 183    }
 184
 185    public static PreparedExperimentRunMetadata BuildRunMetadata(
 186        PreparedExperimentManifest manifest,
 187        PreparedExperimentRunOptions options)
 188    {
 1189        ArgumentNullException.ThrowIfNull(manifest);
 1190        ArgumentNullException.ThrowIfNull(options);
 191
 1192        var explicitEvaluationTime = EvaluationTimeParser.ParseOrNull(options.EvaluationTime);
 1193        EvaluationTimestampPolicy? evaluationTimestampPolicy = null;
 194        string evaluationTimestampPolicyKey;
 195
 1196        if (explicitEvaluationTime is null)
 197        {
 0198            var kind = string.IsNullOrWhiteSpace(options.EvaluationPolicyKind)
 0199                ? EvaluationTimestampPolicy.RelativeKind
 0200                : options.EvaluationPolicyKind;
 0201            var offset = string.IsNullOrWhiteSpace(options.EvaluationPolicyOffset)
 0202                ? "-12:00:00"
 0203                : options.EvaluationPolicyOffset;
 0204            evaluationTimestampPolicy = EvaluationTimestampPolicyParser.Parse(kind, offset);
 0205            evaluationTimestampPolicyKey = ExperimentArtifactSupport.BuildRelativeEvaluationPolicyKey(evaluationTimestam
 206        }
 207        else
 208        {
 1209            evaluationTimestampPolicyKey = "exact-time";
 210        }
 211
 1212        var normalizedReasoningEffort = string.IsNullOrWhiteSpace(options.ReasoningEffort)
 1213            ? null
 1214            : options.ReasoningEffort.Trim().ToLowerInvariant();
 1215        var runSubjectDisplayName = BuildRunSubjectDisplayName(options.Model, normalizedReasoningEffort);
 1216        var runSubjectId = normalizedReasoningEffort is null
 1217            ? options.Model
 1218            : $"{options.Model}:reasoning-effort:{normalizedReasoningEffort}";
 219
 1220        return new PreparedExperimentRunMetadata
 1221        {
 1222            Runner = "match-experiment-runner",
 1223            TaskType = ResolveTaskType(manifest),
 1224            CommunityContext = ResolveCommunityContext(manifest),
 1225            Competition = manifest.Competition,
 1226            SourceDatasetName = manifest.SourceDatasetName,
 1227            DatasetName = options.DatasetName ?? manifest.SliceDatasetName,
 1228            PromptKey = options.PromptKey,
 1229            PromptSource = options.PromptSource,
 1230            LangfusePromptName = options.LangfusePromptName,
 1231            LangfusePromptLabel = options.LangfusePromptLabel,
 1232            LangfusePromptVersion = options.LangfusePromptVersion,
 1233            ReasoningEffort = normalizedReasoningEffort,
 1234            MaxOutputTokenCount = options.MaxOutputTokenCount,
 1235            SliceKind = ResolveSliceKind(manifest),
 1236            SliceKey = manifest.SliceKey,
 1237            SourcePoolKey = manifest.SourcePoolKey,
 1238            SelectedItemIdsHash = string.IsNullOrWhiteSpace(manifest.SelectedItemIdsHash)
 1239                ? ExperimentArtifactSupport.ComputeSelectedItemIdsHash(
 1240                    manifest.SelectedItemIds.Count > 0
 1241                        ? manifest.SelectedItemIds
 1242                        : manifest.Items.Select(item => item.SliceDatasetItemId))
 1243                : manifest.SelectedItemIdsHash,
 1244            SelectedItemIdsCount = manifest.SelectedItemIds.Count > 0 ? manifest.SelectedItemIds.Count : manifest.Items.
 1245            SampleSize = manifest.SampleSize > 0 ? manifest.SampleSize : manifest.Items.Count,
 1246            MatchCount = manifest.MatchCount,
 1247            Repetitions = manifest.Repetitions,
 1248            EvaluationTimestampPolicyKey = evaluationTimestampPolicyKey,
 1249            EvaluationTimestampPolicy = evaluationTimestampPolicy is null
 1250                ? null
 1251                : new PreparedExperimentEvaluationTimestampPolicyMetadata
 1252                {
 1253                    Kind = evaluationTimestampPolicy.Kind,
 1254                    Reference = evaluationTimestampPolicy.Reference,
 1255                    Offset = evaluationTimestampPolicy.Offset.ToTimeSpan().ToString("c", CultureInfo.InvariantCulture)
 1256                },
 1257            EvaluationTime = explicitEvaluationTime?.ToString("O", CultureInfo.InvariantCulture),
 1258            StartedAtUtc = ExperimentArtifactSupport.FormatStartedAtUtc(DateTimeOffset.UtcNow),
 1259            SampleSeed = manifest.SampleSeed,
 1260            SampleMethod = ResolveSampleMethod(manifest),
 1261            IncludeJustification = options.IncludeJustification,
 1262            PromptVersion = options.PromptKey,
 1263            SourceDatasetKind = DeriveSourceDatasetKind(manifest),
 1264            DatasetItemIdMap = CreateDatasetItemIdMap(manifest),
 1265            Model = options.Model,
 1266            ObservationName = "predict-match",
 1267            RunSubjectKind = "model",
 1268            RunSubjectId = runSubjectId,
 1269            RunSubjectDisplayName = runSubjectDisplayName,
 1270            BatchStrategy = options.BatchStrategy,
 1271            BatchSize = options.BatchSize,
 1272            BatchCount = options.BatchCount,
 1273            Parallelism = options.Parallelism
 1274        };
 275    }
 276
 277    public static IReadOnlyDictionary<string, string> CreateDatasetItemIdMap(PreparedExperimentManifest manifest)
 278    {
 1279        var groupedItems = manifest.Items
 1280                .GroupBy(item => item.SourceDatasetItemId, StringComparer.Ordinal)
 1281            .ToList();
 282
 1283        if (groupedItems.Any(group => group.Count() != 1))
 284        {
 0285            return new Dictionary<string, string>();
 286        }
 287
 1288        return groupedItems.ToDictionary(
 1289            group => group.Key,
 1290            group => group.Single().SliceDatasetItemId,
 1291            StringComparer.Ordinal);
 292    }
 293
 294    public static IReadOnlyList<string> DeriveTraceTags(PreparedExperimentRunMetadata runMetadata)
 295    {
 1296        var tags = new List<string>();
 297
 1298        if (!string.IsNullOrWhiteSpace(runMetadata.TaskType))
 299        {
 1300            tags.Add($"task:{runMetadata.TaskType}");
 301        }
 302
 1303        if (!string.IsNullOrWhiteSpace(runMetadata.CommunityContext))
 304        {
 1305            tags.Add($"community:{runMetadata.CommunityContext}");
 306        }
 307
 1308        if (!string.IsNullOrWhiteSpace(runMetadata.SliceKey))
 309        {
 1310            tags.Add($"slice:{runMetadata.SliceKey}");
 311        }
 312
 1313        if (!string.IsNullOrWhiteSpace(runMetadata.Model))
 314        {
 1315            tags.Add($"model:{runMetadata.Model}");
 316        }
 317
 1318        if (!string.IsNullOrWhiteSpace(runMetadata.SliceKind))
 319        {
 1320            tags.Add($"slice-kind:{runMetadata.SliceKind}");
 321        }
 322
 1323        if (runMetadata.MatchCount is int matchCount)
 324        {
 0325            tags.Add($"match-count:{matchCount.ToString(CultureInfo.InvariantCulture)}");
 326        }
 327
 1328        if (runMetadata.Repetitions is int repetitions)
 329        {
 0330            tags.Add($"repetitions:{repetitions.ToString(CultureInfo.InvariantCulture)}");
 331        }
 332
 1333        if (!string.IsNullOrWhiteSpace(runMetadata.PromptKey))
 334        {
 1335            tags.Add($"prompt:{runMetadata.PromptKey}");
 336        }
 337
 1338        if (!string.IsNullOrWhiteSpace(runMetadata.PromptSource))
 339        {
 1340            tags.Add($"prompt-source:{runMetadata.PromptSource}");
 341        }
 342
 1343        if (!string.IsNullOrWhiteSpace(runMetadata.LangfusePromptName))
 344        {
 1345            tags.Add($"langfuse-prompt:{runMetadata.LangfusePromptName}");
 346        }
 347
 1348        if (!string.IsNullOrWhiteSpace(runMetadata.LangfusePromptLabel))
 349        {
 1350            tags.Add($"langfuse-prompt-label:{runMetadata.LangfusePromptLabel}");
 351        }
 352
 1353        if (runMetadata.LangfusePromptVersion is not null)
 354        {
 1355            tags.Add($"langfuse-prompt-version:{runMetadata.LangfusePromptVersion}");
 356        }
 357
 1358        if (!string.IsNullOrWhiteSpace(runMetadata.ReasoningEffort))
 359        {
 1360            tags.Add($"reasoning-effort:{runMetadata.ReasoningEffort}");
 361        }
 362
 1363        if (runMetadata.MaxOutputTokenCount is not null)
 364        {
 1365            tags.Add($"max-output-tokens:{runMetadata.MaxOutputTokenCount}");
 366        }
 367
 1368        return tags.Distinct(StringComparer.Ordinal).ToList();
 369    }
 370
 371    public static IReadOnlyDictionary<string, string> DerivePropagatedMetadata(PreparedExperimentRunMetadata runMetadata
 372    {
 1373        var metadata = new Dictionary<string, string>(StringComparer.Ordinal);
 1374        AddIfValid(metadata, "communityContext", runMetadata.CommunityContext);
 1375        AddIfValid(metadata, "evaluationTime", runMetadata.EvaluationTime);
 1376        AddIfValid(metadata, "evaluationTimestampPolicyKey", runMetadata.EvaluationTimestampPolicyKey);
 1377        AddIfValid(metadata, "model", runMetadata.Model);
 1378        AddIfValid(metadata, "promptKey", runMetadata.PromptKey);
 1379        AddIfValid(metadata, "promptSource", runMetadata.PromptSource);
 1380        AddIfValid(metadata, "langfusePromptName", runMetadata.LangfusePromptName);
 1381        AddIfValid(metadata, "langfusePromptLabel", runMetadata.LangfusePromptLabel);
 1382        AddIfValid(metadata, "langfusePromptVersion", runMetadata.LangfusePromptVersion?.ToString(CultureInfo.InvariantC
 1383        AddIfValid(metadata, "reasoningEffort", runMetadata.ReasoningEffort);
 1384        AddIfValid(metadata, "maxOutputTokens", runMetadata.MaxOutputTokenCount?.ToString(CultureInfo.InvariantCulture))
 1385        AddIfValid(metadata, "sampleMethod", runMetadata.SampleMethod);
 1386        AddIfValid(metadata, "matchCount", runMetadata.MatchCount?.ToString(CultureInfo.InvariantCulture));
 1387        AddIfValid(metadata, "repetitions", runMetadata.Repetitions?.ToString(CultureInfo.InvariantCulture));
 1388        AddIfValid(metadata, "parallelism", runMetadata.Parallelism?.ToString(CultureInfo.InvariantCulture));
 1389        AddIfValid(metadata, "selectedItemIdsHash", runMetadata.SelectedItemIdsHash);
 1390        AddIfValid(metadata, "sliceKind", runMetadata.SliceKind);
 1391        AddIfValid(metadata, "sliceKey", runMetadata.SliceKey);
 1392        AddIfValid(metadata, "startedAtUtc", runMetadata.StartedAtUtc);
 1393        AddIfValid(metadata, "task", runMetadata.TaskType);
 1394        AddIfValid(metadata, "observationName", runMetadata.ObservationName);
 1395        AddIfValid(metadata, "runSubjectKind", runMetadata.RunSubjectKind);
 1396        AddIfValid(metadata, "runSubjectId", runMetadata.RunSubjectId);
 1397        AddIfValid(metadata, "runSubjectDisplayName", runMetadata.RunSubjectDisplayName);
 1398        return metadata;
 399    }
 400
 401    public static string BuildRunSubjectDisplayName(string model, string? reasoningEffort)
 402    {
 1403        return string.IsNullOrWhiteSpace(reasoningEffort)
 1404            ? model
 1405            : $"{model} ({reasoningEffort.Trim().ToLowerInvariant()})";
 406    }
 407
 408    public static string ResolveTaskType(PreparedExperimentManifest manifest)
 409    {
 1410        if (!string.IsNullOrWhiteSpace(manifest.TaskType))
 411        {
 1412            return manifest.TaskType;
 413        }
 414
 1415        var sliceKind = ResolveSliceKind(manifest);
 1416        var sampleMethod = ResolveSampleMethod(manifest);
 417
 1418        if (string.Equals(sliceKind, "community-to-date", StringComparison.OrdinalIgnoreCase)
 1419            || string.Equals(sampleMethod, "community-to-date", StringComparison.OrdinalIgnoreCase))
 420        {
 0421            return "community-to-date";
 422        }
 423
 1424        if (string.Equals(sliceKind, "repeated-match-slice", StringComparison.OrdinalIgnoreCase)
 1425            || string.Equals(sampleMethod, "repeated-match-slice", StringComparison.OrdinalIgnoreCase))
 426        {
 0427            return "repeated-match-slice";
 428        }
 429
 1430        if (string.Equals(sliceKind, "single-match", StringComparison.OrdinalIgnoreCase)
 1431            || string.Equals(sliceKind, "repeated-match", StringComparison.OrdinalIgnoreCase)
 1432            || string.Equals(sampleMethod, "repeat-single-match", StringComparison.OrdinalIgnoreCase)
 1433            || string.Equals(sampleMethod, "repeated-match", StringComparison.OrdinalIgnoreCase))
 434        {
 1435            return "repeated-match";
 436        }
 437
 1438        return "slice";
 439    }
 440
 441    public static void ReportProgress(string message)
 442    {
 1443        Console.Error.WriteLine($"[progress] {message}");
 1444    }
 445
 446    private static void AddIfValid(IDictionary<string, string> metadata, string key, string? value)
 447    {
 1448        if (!string.IsNullOrWhiteSpace(value) && value.Length <= 200)
 449        {
 1450            metadata[key] = value;
 451        }
 1452    }
 453
 454    private static string DeriveSourceDatasetKind(PreparedExperimentManifest manifest)
 455    {
 1456        return ResolveTaskType(manifest);
 457    }
 458
 459    private static string ResolveCommunityContext(PreparedExperimentManifest manifest)
 460    {
 1461        if (!string.IsNullOrWhiteSpace(manifest.CommunityContext))
 462        {
 1463            return manifest.CommunityContext;
 464        }
 465
 0466        if (string.IsNullOrWhiteSpace(manifest.SourceDatasetName))
 467        {
 0468            throw new InvalidOperationException("Slice manifest must contain communityContext or sourceDatasetName.");
 469        }
 470
 0471        return manifest.SourceDatasetName
 0472            .Split('/', StringSplitOptions.RemoveEmptyEntries)
 0473            .Last();
 474    }
 475
 476    private static string ResolveSampleMethod(PreparedExperimentManifest manifest)
 477    {
 1478        return string.IsNullOrWhiteSpace(manifest.SampleMethod)
 1479            ? "random-sample"
 1480            : manifest.SampleMethod;
 481    }
 482
 483    private static string ResolveSliceKind(PreparedExperimentManifest manifest)
 484    {
 1485        return string.IsNullOrWhiteSpace(manifest.SliceKind)
 1486            ? "random-sample"
 1487            : manifest.SliceKind;
 488    }
 489
 490    private static int? TryParseRepeatedMatchSliceRepetition(string datasetItemId)
 491    {
 0492        var markerIndex = datasetItemId.IndexOf("__repeated-match-slice__", StringComparison.Ordinal);
 0493        if (markerIndex < 0)
 494        {
 0495            return null;
 496        }
 497
 0498        var lastSeparatorIndex = datasetItemId.LastIndexOf("__", StringComparison.Ordinal);
 0499        if (lastSeparatorIndex < 0 || lastSeparatorIndex + 2 >= datasetItemId.Length)
 500        {
 0501            return null;
 502        }
 503
 0504        return int.TryParse(
 0505            datasetItemId[(lastSeparatorIndex + 2)..],
 0506            NumberStyles.Integer,
 0507            CultureInfo.InvariantCulture,
 0508            out var repetitionIndex)
 0509            ? repetitionIndex
 0510            : null;
 511    }
 512}

Methods/Properties

CreateBatchChunks<T>(System.Collections.Generic.IReadOnlyList<T>, int)
CreateWarmupThenBatchChunks<T>(System.Collections.Generic.IReadOnlyList<T>, int)
CalculateScores(EHonda.KicktippAi.Core.Prediction, int, int)
SummarizeScores(System.Collections.Generic.IReadOnlyList<Orchestrator.Commands.Observability.Experiments.ExperimentItemScores>)
SummarizeExecutionScores(System.Collections.Generic.IReadOnlyList<Orchestrator.Commands.Observability.Experiments.PreparedExperimentExecutionSummary>, string)
DeriveExperimentName(Orchestrator.Commands.Observability.Experiments.PreparedExperimentRunMetadata, string)
BuildLangfuseExperimentMetadata(Orchestrator.Commands.Observability.Experiments.PreparedExperimentRunMetadata, string, string, System.Collections.Generic.IReadOnlyDictionary<string, string>)
CreateScoreId(string, string[])
BuildRunMetadata(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest, Orchestrator.Commands.Observability.Experiments.PreparedExperimentRunOptions)
CreateDatasetItemIdMap(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest)
DeriveTraceTags(Orchestrator.Commands.Observability.Experiments.PreparedExperimentRunMetadata)
DerivePropagatedMetadata(Orchestrator.Commands.Observability.Experiments.PreparedExperimentRunMetadata)
BuildRunSubjectDisplayName(string, string)
ResolveTaskType(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest)
ReportProgress(string)
AddIfValid(System.Collections.Generic.IDictionary<string, string>, string, string)
DeriveSourceDatasetKind(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest)
ResolveCommunityContext(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest)
ResolveSampleMethod(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest)
ResolveSliceKind(Orchestrator.Commands.Observability.Experiments.PreparedExperimentManifest)
TryParseRepeatedMatchSliceRepetition(string)