< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Langfuse.LangfuseScenario
Assembly: NexusLabs.Needlr.AgentFramework.Langfuse
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Langfuse/LangfuseScenario.cs
Line coverage
86%
Covered lines: 63
Uncovered lines: 10
Coverable lines: 73
Total lines: 173
Line coverage: 86.3%
Branch coverage
53%
Covered branches: 31
Total branches: 58
Branch coverage: 53.4%
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_TraceId()50%22100%
get_Activity()100%11100%
RecordScoreAsync(...)50%44100%
RecordScoreAsync(...)0%2040%
RecordScoreAsync(...)0%2040%
RecordEvaluationAsync(...)50%44100%
Dispose()50%22100%
SetTracePublic(...)50%22100%
SetVersion(...)50%22100%
SetInput(...)50%22100%
SetOutput(...)50%22100%
ToAttributeValue(...)100%22100%
RecordSessionScoreAsync(...)50%44100%
RecordSessionScoreAsync(...)50%44100%
RecordSessionScoreAsync(...)0%2040%
SkipSessionScore(...)100%11100%
ApplyTraceAttributes(...)93.75%161694.44%

File(s)

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

#LineLine coverage
 1using System.Diagnostics;
 2using System.Text.Json;
 3
 4using Microsoft.Extensions.AI.Evaluation;
 5
 6namespace NexusLabs.Needlr.AgentFramework.Langfuse;
 7
 8/// <summary>
 9/// Active <see cref="ILangfuseScenario"/> backed by a root OpenTelemetry span and a shared
 10/// <see cref="LangfuseScoreRecorder"/>.
 11/// </summary>
 12internal sealed class LangfuseScenario : ILangfuseScenario
 13{
 14    private readonly Activity? _activity;
 15    private readonly LangfuseScoreRecorder _recorder;
 16    private readonly string? _sessionId;
 17
 1218    public LangfuseScenario(
 1219        LangfuseScoreRecorder recorder,
 1220        string name,
 1221        string? sessionId,
 1222        string? userId,
 1223        IEnumerable<string>? tags,
 1224        IReadOnlyDictionary<string, string>? metadata)
 25    {
 1226        ArgumentNullException.ThrowIfNull(recorder);
 1227        ArgumentException.ThrowIfNullOrWhiteSpace(name);
 28
 1229        _recorder = recorder;
 1230        _sessionId = sessionId;
 1231        _activity = LangfuseActivitySource.Source.StartActivity(name, ActivityKind.Internal);
 32
 1233        ApplyTraceAttributes(_activity, name, sessionId, userId, tags, metadata);
 1234    }
 35
 36    /// <inheritdoc />
 1137    public string? TraceId => _activity?.TraceId.ToString();
 38
 39    /// <inheritdoc />
 240    public Activity? Activity => _activity;
 41
 42    /// <inheritdoc />
 43    public Task RecordScoreAsync(string name, double value, string? comment = null, CancellationToken cancellationToken 
 344        TraceId is { Length: > 0 } id
 345            ? _recorder.RecordNumericAsync(id, name, value, comment, cancellationToken)
 346            : _recorder.RecordSkippedAsync(name, cancellationToken);
 47
 48    /// <inheritdoc />
 49    public Task RecordScoreAsync(string name, bool value, string? comment = null, CancellationToken cancellationToken = 
 050        TraceId is { Length: > 0 } id
 051            ? _recorder.RecordBooleanAsync(id, name, value, comment, cancellationToken)
 052            : _recorder.RecordSkippedAsync(name, cancellationToken);
 53
 54    /// <inheritdoc />
 55    public Task RecordScoreAsync(string name, string value, string? comment = null, CancellationToken cancellationToken 
 056        TraceId is { Length: > 0 } id
 057            ? _recorder.RecordCategoricalAsync(id, name, value, comment, cancellationToken)
 058            : _recorder.RecordSkippedAsync(name, cancellationToken);
 59
 60    /// <inheritdoc />
 61    public Task RecordEvaluationAsync(EvaluationResult result, CancellationToken cancellationToken = default)
 62    {
 163        ArgumentNullException.ThrowIfNull(result);
 64
 165        return TraceId is { Length: > 0 } id
 166            ? _recorder.RecordEvaluationAsync(id, result, cancellationToken)
 167            : _recorder.RecordSkippedAsync("evaluation", cancellationToken);
 68    }
 69
 70    /// <inheritdoc />
 1271    public void Dispose() => _activity?.Dispose();
 72
 73    /// <inheritdoc />
 74    public void SetTracePublic(bool isPublic = true) =>
 175        _activity?.SetTag("langfuse.trace.public", isPublic);
 76
 77    /// <inheritdoc />
 78    public void SetVersion(string version)
 79    {
 180        ArgumentException.ThrowIfNullOrWhiteSpace(version);
 181        _activity?.SetTag("langfuse.version", version);
 182    }
 83
 84    /// <inheritdoc />
 85    public void SetInput(object input)
 86    {
 187        ArgumentNullException.ThrowIfNull(input);
 188        _activity?.SetTag("langfuse.trace.input", ToAttributeValue(input));
 189    }
 90
 91    /// <inheritdoc />
 92    public void SetOutput(object output)
 93    {
 194        ArgumentNullException.ThrowIfNull(output);
 195        _activity?.SetTag("langfuse.trace.output", ToAttributeValue(output));
 196    }
 97
 98    private static string ToAttributeValue(object value) =>
 299        value as string ?? JsonSerializer.Serialize(value);
 100
 101    /// <inheritdoc />
 102    public Task RecordSessionScoreAsync(string name, double value, string? comment = null, CancellationToken cancellatio
 1103        _sessionId is { Length: > 0 } sid
 1104            ? _recorder.RecordNumericAsync(LangfuseScoreTarget.Session(sid), name, value, comment, cancellationToken)
 1105            : SkipSessionScore(name, cancellationToken);
 106
 107    /// <inheritdoc />
 108    public Task RecordSessionScoreAsync(string name, bool value, string? comment = null, CancellationToken cancellationT
 1109        _sessionId is { Length: > 0 } sid
 1110            ? _recorder.RecordBooleanAsync(LangfuseScoreTarget.Session(sid), name, value, comment, cancellationToken)
 1111            : SkipSessionScore(name, cancellationToken);
 112
 113    /// <inheritdoc />
 114    public Task RecordSessionScoreAsync(string name, string value, string? comment = null, CancellationToken cancellatio
 0115        _sessionId is { Length: > 0 } sid
 0116            ? _recorder.RecordCategoricalAsync(LangfuseScoreTarget.Session(sid), name, value, comment, cancellationToken
 0117            : SkipSessionScore(name, cancellationToken);
 118
 119    private Task SkipSessionScore(string name, CancellationToken cancellationToken) =>
 1120        _recorder.RecordSkippedAsync(
 1121            name,
 1122            $"Cannot record session score '{name}': this scenario has no session id. " +
 1123            "Pass a sessionId when beginning the scenario to enable session-level scoring.",
 1124            cancellationToken);
 125
 126    private static void ApplyTraceAttributes(
 127        Activity? activity,
 128        string name,
 129        string? sessionId,
 130        string? userId,
 131        IEnumerable<string>? tags,
 132        IReadOnlyDictionary<string, string>? metadata)
 133    {
 12134        if (activity is null)
 135        {
 0136            return;
 137        }
 138
 12139        activity.SetTag("langfuse.trace.name", name);
 140
 12141        if (!string.IsNullOrWhiteSpace(sessionId))
 142        {
 4143            activity.SetTag("langfuse.session.id", sessionId);
 4144            activity.SetBaggage("session.id", sessionId);
 145        }
 146
 12147        if (!string.IsNullOrWhiteSpace(userId))
 148        {
 3149            activity.SetTag("langfuse.user.id", userId);
 3150            activity.SetBaggage("user.id", userId);
 151        }
 152
 12153        if (tags is not null)
 154        {
 3155            var tagArray = tags.Where(t => !string.IsNullOrWhiteSpace(t)).ToArray();
 1156            if (tagArray.Length > 0)
 157            {
 1158                activity.SetTag("langfuse.trace.tags", tagArray);
 159            }
 160        }
 161
 12162        if (metadata is not null)
 163        {
 4164            foreach (var entry in metadata)
 165            {
 1166                if (!string.IsNullOrWhiteSpace(entry.Key))
 167                {
 1168                    activity.SetTag($"langfuse.trace.metadata.{entry.Key}", entry.Value);
 169                }
 170            }
 171        }
 12172    }
 173}