< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Langfuse.LangfuseExperimentRun
Assembly: NexusLabs.Needlr.AgentFramework.Langfuse
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Langfuse/LangfuseExperimentRun.cs
Line coverage
94%
Covered lines: 51
Uncovered lines: 3
Coverable lines: 54
Total lines: 101
Line coverage: 94.4%
Branch coverage
40%
Covered branches: 4
Total branches: 10
Branch coverage: 40%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_DatasetName()100%11100%
get_RunName()100%11100%
BeginItemAsync()37.5%8883.33%
LinkRunItemAsync()50%22100%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Langfuse/LangfuseExperimentRun.cs

#LineLine coverage
 1namespace NexusLabs.Needlr.AgentFramework.Langfuse;
 2
 3/// <summary>
 4/// Default <see cref="ILangfuseExperimentRun"/>. Starts a scenario per dataset item and links its
 5/// trace to the run via <c>POST /api/public/dataset-run-items</c>. Link failures are non-fatal —
 6/// routed to the diagnostics callback — so a Langfuse hiccup does not crash the eval; the gap is
 7/// surfaced rather than silently swallowed.
 8/// </summary>
 9internal sealed class LangfuseExperimentRun : ILangfuseExperimentRun
 10{
 11    private readonly LangfuseApiClient _apiClient;
 12    private readonly LangfuseScoreRecorder _recorder;
 13    private readonly string? _runDescription;
 14    private readonly Action<string>? _diagnostics;
 15
 216    public LangfuseExperimentRun(
 217        LangfuseApiClient apiClient,
 218        LangfuseScoreRecorder recorder,
 219        string datasetName,
 220        string runName,
 221        string? runDescription,
 222        Action<string>? diagnostics)
 23    {
 224        ArgumentNullException.ThrowIfNull(apiClient);
 225        ArgumentNullException.ThrowIfNull(recorder);
 226        ArgumentException.ThrowIfNullOrWhiteSpace(datasetName);
 227        ArgumentException.ThrowIfNullOrWhiteSpace(runName);
 28
 229        _apiClient = apiClient;
 230        _recorder = recorder;
 231        _runDescription = runDescription;
 232        _diagnostics = diagnostics;
 233        DatasetName = datasetName;
 234        RunName = runName;
 235    }
 36
 37    /// <inheritdoc />
 238    public string DatasetName { get; }
 39
 40    /// <inheritdoc />
 341    public string RunName { get; }
 42
 43    /// <inheritdoc />
 44    public async Task<ILangfuseScenario> BeginItemAsync(
 45        string datasetItemId,
 46        string? scenarioName = null,
 47        IEnumerable<string>? tags = null,
 48        IReadOnlyDictionary<string, string>? metadata = null,
 49        CancellationToken cancellationToken = default)
 50    {
 251        ArgumentException.ThrowIfNullOrWhiteSpace(datasetItemId);
 52
 253        var name = string.IsNullOrWhiteSpace(scenarioName)
 254            ? $"{DatasetName}: {datasetItemId}"
 255            : scenarioName;
 56
 257        var scenario = new LangfuseScenario(
 258            _recorder,
 259            name,
 260            sessionId: null,
 261            userId: null,
 262            tags,
 263            metadata);
 64
 265        if (scenario.TraceId is { Length: > 0 } traceId)
 66        {
 267            await LinkRunItemAsync(datasetItemId, traceId, cancellationToken).ConfigureAwait(false);
 68        }
 69        else
 70        {
 071            _diagnostics?.Invoke(
 072                $"Langfuse dataset run item skipped for item '{datasetItemId}' in run '{RunName}': " +
 073                "no sampled trace was available to link.");
 74        }
 75
 276        return scenario;
 277    }
 78
 79    private async Task LinkRunItemAsync(string datasetItemId, string traceId, CancellationToken cancellationToken)
 80    {
 81        try
 82        {
 283            var request = new LangfuseCreateDatasetRunItemRequest
 284            {
 285                RunName = RunName,
 286                RunDescription = _runDescription,
 287                DatasetItemId = datasetItemId,
 288                TraceId = traceId,
 289            };
 90
 291            await _apiClient
 292                .PostAsync("api/public/dataset-run-items", request, cancellationToken)
 293                .ConfigureAwait(false);
 194        }
 195        catch (LangfuseException ex)
 96        {
 197            _diagnostics?.Invoke(
 198                $"Langfuse dataset run item link failed for item '{datasetItemId}' in run '{RunName}': {ex.Message}");
 199        }
 2100    }
 101}