< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Langfuse.LangfuseOptions
Assembly: NexusLabs.Needlr.AgentFramework.Langfuse
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Langfuse/LangfuseOptions.cs
Line coverage
100%
Covered lines: 34
Uncovered lines: 0
Coverable lines: 34
Total lines: 239
Line coverage: 100%
Branch coverage
100%
Covered branches: 10
Total branches: 10
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_PublicKey()100%11100%
get_SecretKey()100%11100%
get_Host()100%11100%
get_Region()100%11100%
get_Enabled()100%11100%
get_ServiceName()100%11100%
get_ServiceVersion()100%11100%
get_Environment()100%11100%
get_Release()100%11100%
get_IncludeMetrics()100%11100%
get_ScoreFailureMode()100%11100%
get_ScoreErrorCallback()100%11100%
get_NormalizeScoreNames()100%11100%
get_DiagnosticsCallback()100%11100%
get_SamplingRatio()100%11100%
get_AgentActivitySourceName()100%11100%
get_AgentMeterName()100%11100%
get_GenAiMeterName()100%11100%
get_AdditionalActivitySources()100%11100%
get_AdditionalMeters()100%11100%
get_HasCredentials()100%44100%
get_HasExplicitTarget()100%22100%
get_IsConfigured()100%22100%
FromEnvironment()100%11100%
NullIfBlank(...)100%22100%

File(s)

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

#LineLine coverage
 1using NexusLabs.Needlr.AgentFramework.Diagnostics;
 2
 3namespace NexusLabs.Needlr.AgentFramework.Langfuse;
 4
 5/// <summary>
 6/// Configuration for exporting Needlr agent telemetry and evaluation scores to Langfuse.
 7/// </summary>
 8/// <remarks>
 9/// <para>
 10/// The common path is <see cref="FromEnvironment"/>, which reads the standard Langfuse
 11/// environment variables. When the public/secret keys are absent the integration disables
 12/// itself and behaves as a no-op, so credential-less CI runs never fail.
 13/// </para>
 14/// <para>
 15/// Needlr emits OpenTelemetry traces and metrics under well-known source and meter names.
 16/// The defaults here match <see cref="AgentFrameworkMetricsOptions"/>. If a consumer has
 17/// customised those names via <c>ConfigureMetrics(...)</c>, set
 18/// <see cref="AgentActivitySourceName"/>, <see cref="AgentMeterName"/>, and
 19/// <see cref="GenAiMeterName"/> to the same values, or add them through
 20/// <see cref="AdditionalActivitySources"/> / <see cref="AdditionalMeters"/>.
 21/// </para>
 22/// </remarks>
 23public sealed class LangfuseOptions
 24{
 125    private static readonly AgentFrameworkMetricsOptions MetricsDefaults = new();
 26
 27    /// <summary>
 28    /// Environment variable read for the Langfuse public key by <see cref="FromEnvironment"/>.
 29    /// </summary>
 30    public const string PublicKeyEnvironmentVariable = "LANGFUSE_PUBLIC_KEY";
 31
 32    /// <summary>
 33    /// Environment variable read for the Langfuse secret key by <see cref="FromEnvironment"/>.
 34    /// </summary>
 35    public const string SecretKeyEnvironmentVariable = "LANGFUSE_SECRET_KEY";
 36
 37    /// <summary>
 38    /// Environment variable read for the Langfuse host (base URL) by <see cref="FromEnvironment"/>.
 39    /// </summary>
 40    public const string HostEnvironmentVariable = "LANGFUSE_HOST";
 41
 42    /// <summary>
 43    /// Gets or sets the Langfuse public key (<c>pk-lf-...</c>). Required for export.
 44    /// </summary>
 6245    public string? PublicKey { get; set; }
 46
 47    /// <summary>
 48    /// Gets or sets the Langfuse secret key (<c>sk-lf-...</c>). Required for export.
 49    /// </summary>
 5450    public string? SecretKey { get; set; }
 51
 52    /// <summary>
 53    /// Gets or sets the Langfuse base URL (for example <c>https://cloud.langfuse.com</c> or a
 54    /// self-hosted <c>http://localhost:3000</c>). When set, this takes precedence over
 55    /// <see cref="Region"/>. When <see langword="null"/>, the URL is derived from
 56    /// <see cref="Region"/>.
 57    /// </summary>
 3258    public string? Host { get; set; }
 59
 60    /// <summary>
 61    /// Gets or sets the Langfuse Cloud data region. <see langword="null"/> by default — exporting
 62    /// to a cloud region is therefore an explicit, opt-in choice. Ignored when <see cref="Host"/>
 63    /// is set.
 64    /// </summary>
 65    /// <remarks>
 66    /// To avoid silently sending traces (which may include prompts, agent outputs, and customer
 67    /// data) to Langfuse Cloud, this integration requires an <strong>explicit</strong> target: set
 68    /// <see cref="Host"/> for a self-hosted deployment, or set <see cref="Region"/> to deliberately
 69    /// opt in to a Langfuse Cloud region. When neither is set, export is disabled even if
 70    /// credentials are present (see <see cref="IsConfigured"/>) and a message is sent to
 71    /// <see cref="DiagnosticsCallback"/>.
 72    /// </remarks>
 2373    public LangfuseRegion? Region { get; set; }
 74
 75    /// <summary>
 76    /// Gets or sets a value indicating whether export is enabled. When <see langword="true"/>
 77    /// (the default) export still only occurs if <see cref="PublicKey"/> and
 78    /// <see cref="SecretKey"/> are both present — see <see cref="IsConfigured"/>. Set to
 79    /// <see langword="false"/> to force a no-op regardless of credentials.
 80    /// </summary>
 5481    public bool Enabled { get; set; } = true;
 82
 83    /// <summary>
 84    /// Gets or sets the OpenTelemetry <c>service.name</c> resource attribute applied to exported
 85    /// telemetry. Surfaces in Langfuse as the originating service. Defaults to
 86    /// <c>"needlr-agent"</c>.
 87    /// </summary>
 2988    public string ServiceName { get; set; } = "needlr-agent";
 89
 90    /// <summary>
 91    /// Gets or sets the optional OpenTelemetry <c>service.version</c> resource attribute.
 92    /// </summary>
 193    public string? ServiceVersion { get; set; }
 94
 95    /// <summary>
 96    /// Gets or sets the Langfuse deployment environment (for example <c>ci</c>, <c>local</c>,
 97    /// <c>staging</c>, or <c>production</c>). When set, it is emitted as <c>langfuse.environment</c>
 98    /// on every exported span so Langfuse partitions this run's data — keeping CI eval noise out of
 99    /// production dashboards. <see langword="null"/> by default (Langfuse uses its <c>default</c>
 100    /// environment).
 101    /// </summary>
 1102    public string? Environment { get; set; }
 103
 104    /// <summary>
 105    /// Gets or sets the application release identifier (for example a git SHA or semantic version).
 106    /// When set, it is emitted as <c>langfuse.release</c> on every exported span so scores, cost,
 107    /// and latency can be compared across releases. <see langword="null"/> by default.
 108    /// </summary>
 1109    public string? Release { get; set; }
 110
 111    /// <summary>
 112    /// Gets or sets a value indicating whether Needlr's <c>gen_ai</c> metrics (including the
 113    /// <c>gen_ai.client.token.usage</c> histogram) are exported alongside traces. Defaults to
 114    /// <see langword="false"/>.
 115    /// </summary>
 116    /// <remarks>
 117    /// As of Langfuse v3.x the OTLP metrics endpoint (<c>/api/public/otel/v1/metrics</c>) accepts
 118    /// requests (returns HTTP 200) but does <strong>not</strong> ingest the data — there is no
 119    /// corresponding metrics read API, so exported metrics are silently discarded. Token usage is
 120    /// already carried on the generation spans, so metrics export is off by default. Enable this
 121    /// only if you point the exporter at a backend that ingests OTLP metrics.
 122    /// </remarks>
 2123    public bool IncludeMetrics { get; set; }
 124
 125    /// <summary>
 126    /// Gets or sets how a failed evaluation-score upload is handled. Defaults to
 127    /// <see cref="LangfuseScoreFailureMode.NonFatal"/> so a transient Langfuse outage does not turn
 128    /// a passing eval into a failure.
 129    /// </summary>
 2130    public LangfuseScoreFailureMode ScoreFailureMode { get; set; } = LangfuseScoreFailureMode.NonFatal;
 131
 132    /// <summary>
 133    /// Gets or sets an optional callback invoked when a score upload fails under
 134    /// <see cref="LangfuseScoreFailureMode.NonFatal"/>. Use it to log the loss with your own logger.
 135    /// </summary>
 1136    public Action<LangfuseScoreError>? ScoreErrorCallback { get; set; }
 137
 138    /// <summary>
 139    /// Gets or sets a value indicating whether evaluator metric names are normalised to
 140    /// <c>snake_case</c> before being sent as Langfuse score names. Off by default; names are sent
 141    /// verbatim. Enable for consistent dashboard filtering/grouping.
 142    /// </summary>
 2143    public bool NormalizeScoreNames { get; set; }
 144
 145    /// <summary>
 146    /// Gets or sets an optional callback for library diagnostic messages — for example, the warning
 147    /// emitted when credentials are present but no export target (<see cref="Host"/> or
 148    /// <see cref="Region"/>) was chosen. Wire it to your logger to surface these conditions.
 149    /// </summary>
 3150    public Action<string>? DiagnosticsCallback { get; set; }
 151
 152    /// <summary>
 153    /// Gets or sets the head-based trace sampling ratio in the range <c>0.0</c> to <c>1.0</c>.
 154    /// Defaults to <c>1.0</c> (sample everything), which is appropriate for eval workloads.
 155    /// </summary>
 29156    public double SamplingRatio { get; set; } = 1.0;
 157
 158    /// <summary>
 159    /// Gets or sets the name of Needlr's agent <see cref="System.Diagnostics.ActivitySource"/>
 160    /// to export. Defaults to <see cref="AgentFrameworkMetricsOptions.MeterName"/>'s default.
 161    /// </summary>
 30162    public string AgentActivitySourceName { get; set; } = MetricsDefaults.MeterName;
 163
 164    /// <summary>
 165    /// Gets or sets the name of Needlr's agent <see cref="System.Diagnostics.Metrics.Meter"/>
 166    /// to export. Defaults to <see cref="AgentFrameworkMetricsOptions.MeterName"/>'s default.
 167    /// </summary>
 29168    public string AgentMeterName { get; set; } = MetricsDefaults.MeterName;
 169
 170    /// <summary>
 171    /// Gets or sets the name of the meter that owns the <c>gen_ai.client.token.usage</c>
 172    /// histogram shared by Needlr and MEAI. Defaults to
 173    /// <see cref="AgentFrameworkMetricsOptions.GenAiMeterName"/>'s default
 174    /// (<c>"Experimental.Microsoft.Extensions.AI"</c>).
 175    /// </summary>
 29176    public string GenAiMeterName { get; set; } = MetricsDefaults.GenAiMeterName;
 177
 178    /// <summary>
 179    /// Gets a mutable list of additional <see cref="System.Diagnostics.ActivitySource"/> names
 180    /// to export — for example a host's own source or MAF's agent source.
 181    /// </summary>
 29182    public IList<string> AdditionalActivitySources { get; } = [];
 183
 184    /// <summary>
 185    /// Gets a mutable list of additional <see cref="System.Diagnostics.Metrics.Meter"/> names
 186    /// to export.
 187    /// </summary>
 28188    public IList<string> AdditionalMeters { get; } = [];
 189
 190    /// <summary>
 191    /// Gets a value indicating whether both API keys are present and export is enabled. Does not
 192    /// account for whether an export target was chosen — see <see cref="HasExplicitTarget"/> and
 193    /// <see cref="IsConfigured"/>.
 194    /// </summary>
 195    public bool HasCredentials =>
 23196        Enabled
 23197        && !string.IsNullOrWhiteSpace(PublicKey)
 23198        && !string.IsNullOrWhiteSpace(SecretKey);
 199
 200    /// <summary>
 201    /// Gets a value indicating whether an explicit export target was chosen — either a
 202    /// <see cref="Host"/> (self-hosted) or a <see cref="Region"/> (deliberate Langfuse Cloud
 203    /// opt-in).
 204    /// </summary>
 205    public bool HasExplicitTarget =>
 10206        !string.IsNullOrWhiteSpace(Host) || Region.HasValue;
 207
 208    /// <summary>
 209    /// Gets a value indicating whether the integration is fully configured to export: credentials
 210    /// are present, export is enabled, and an explicit target (<see cref="Host"/> or
 211    /// <see cref="Region"/>) was chosen. Requiring an explicit target prevents accidentally sending
 212    /// traces to Langfuse Cloud.
 213    /// </summary>
 16214    public bool IsConfigured => HasCredentials && HasExplicitTarget;
 215
 216    /// <summary>
 217    /// Builds a <see cref="LangfuseOptions"/> from the standard Langfuse environment variables
 218    /// (<see cref="PublicKeyEnvironmentVariable"/>, <see cref="SecretKeyEnvironmentVariable"/>,
 219    /// and <see cref="HostEnvironmentVariable"/>).
 220    /// </summary>
 221    /// <returns>
 222    /// A populated <see cref="LangfuseOptions"/>. When the keys are absent the result has
 223    /// <see cref="IsConfigured"/> equal to <see langword="false"/> and the integration no-ops.
 224    /// </returns>
 225    public static LangfuseOptions FromEnvironment()
 226    {
 5227        var options = new LangfuseOptions
 5228        {
 5229            PublicKey = NullIfBlank(System.Environment.GetEnvironmentVariable(PublicKeyEnvironmentVariable)),
 5230            SecretKey = NullIfBlank(System.Environment.GetEnvironmentVariable(SecretKeyEnvironmentVariable)),
 5231            Host = NullIfBlank(System.Environment.GetEnvironmentVariable(HostEnvironmentVariable)),
 5232        };
 233
 5234        return options;
 235    }
 236
 237    private static string? NullIfBlank(string? value) =>
 15238        string.IsNullOrWhiteSpace(value) ? null : value;
 239}