Arinti.Langfuse.Tracing.MEAI 1.0.0-alpha.2

This is a prerelease version of Arinti.Langfuse.Tracing.MEAI.
dotnet add package Arinti.Langfuse.Tracing.MEAI --version 1.0.0-alpha.2
                    
NuGet\Install-Package Arinti.Langfuse.Tracing.MEAI -Version 1.0.0-alpha.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Arinti.Langfuse.Tracing.MEAI" Version="1.0.0-alpha.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Arinti.Langfuse.Tracing.MEAI" Version="1.0.0-alpha.2" />
                    
Directory.Packages.props
<PackageReference Include="Arinti.Langfuse.Tracing.MEAI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Arinti.Langfuse.Tracing.MEAI --version 1.0.0-alpha.2
                    
#r "nuget: Arinti.Langfuse.Tracing.MEAI, 1.0.0-alpha.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Arinti.Langfuse.Tracing.MEAI@1.0.0-alpha.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Arinti.Langfuse.Tracing.MEAI&version=1.0.0-alpha.2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Arinti.Langfuse.Tracing.MEAI&version=1.0.0-alpha.2&prerelease
                    
Install as a Cake Tool

Arinti.Langfuse.Tracing.MEAI

An IChatClient middleware that records one Langfuse generation observation per provider request, shaped per the OpenTelemetry GenAI semantic conventions (chat span). Plug it into a ChatClientBuilder pipeline and observability falls out — works with any Microsoft.Extensions.AI adapter (OpenAI, Azure OpenAI, Mistral, Gemini, …).

Usage

using Arinti.Langfuse.Tracing.MEAI;
using Microsoft.Extensions.AI;
using OpenAI;

ApiKeyCredential cred = new(apiKey);

IChatClient baseClient = new OpenAIClient(cred).GetChatClient(modelId).AsIChatClient();

IChatClient client = new ChatClientBuilder(baseClient)
    .UseFunctionInvocation()                                                   // your tool loop
    .Use(next => new LangfuseGenerationChatClient(next))                       // observation
    .Build();

The provider name (for gen_ai.provider.name) and the default model id (for gen_ai.request.model when ChatOptions.ModelId is not set per call) are read from the inner client's ChatClientMetadata. Every first-party MEAI adapter populates these. If a custom adapter doesn't, the span reports gen_ai.provider.name="_OTHER" — the OTel SemConv convention for unenumerated values — and that attribute itself is the diagnostic signal in your tracing UI.

Linking a Langfuse prompt

When the messages come from a Langfuse-managed prompt (fetched via the SDK), attach the prompt reference to the call with WithLangfusePrompt. The middleware stamps langfuse.observation.prompt.name / .version on the generation span, so Langfuse links the observation to the exact prompt version that produced it:

using Arinti.Langfuse.Prompts;      // Compile()
using Arinti.Langfuse.Tracing.MEAI; // WithLangfusePrompt()

ChatPrompt prompt = (ChatPrompt)await prompts.GetPromptAsync("chat.system.default");

ChatOptions options = new ChatOptions { ModelId = modelId }
    .WithLangfusePrompt(prompt);                       // or .WithLangfusePrompt(prompt.Name, prompt.Version)

await client.GetResponseAsync(prompt.Compile(variables), options);

Both the non-streaming and streaming paths carry the tags. Version is optional — pass a name only (.WithLangfusePrompt(prompt.Name)) to link without pinning a version.

This works whenever you build the ChatOptions yourself — including a Semantic Kernel project configured to run on this IChatClient pipeline, as long as you pass the tagged ChatOptions through to the call. When something else owns the span (a manual span you create, or SK's own OTel connector spans), stamp the tags on that span directly with the underlying primitive:

using Arinti.Langfuse.Tracing.ContextPropagation;

activity.SetLangfusePrompt(prompt.Name, prompt.Version);   // `activity` is the span you own

Sensitive data

The middleware records the full conversation on gen_ai.input.messages and the assistant response on gen_ai.output.messages. Both routinely contain raw user content, system prompts, tool results, or model output — material the OTel GenAI SemConv explicitly flags as sensitive. Disable either tag via LangfuseGenerationChatClientOptions:

IChatClient client = new ChatClientBuilder(baseClient)
    .UseFunctionInvocation()
    .Use(next => new LangfuseGenerationChatClient(
        next,
        new LangfuseGenerationChatClientOptions
        {
            RecordInputMessages = false,
            RecordOutputMessages = false,
        }))
    .Build();

Response metadata (id, model, finish reason, usage tokens) is unaffected — only the message payloads are suppressed.

Long-conversation cap

When input messages are recorded, the middleware re-serializes the full chat history into gen_ai.input.messages on every round-trip, which scales O(N²) over the turn count. A soft cap of 50 messages keeps the worst case bounded — beyond that, the oldest messages are elided and a synthetic [N earlier messages truncated] system marker is prepended so the Langfuse view stays accurate about what was dropped. Setting RecordInputMessages = false sidesteps the cost entirely.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-alpha.2 571 7/10/2026
1.0.0-alpha.1 172 6/22/2026
1.0.0-alpha.0 76 6/21/2026