Meilynx.Sdk 0.3.0

dotnet add package Meilynx.Sdk --version 0.3.0
                    
NuGet\Install-Package Meilynx.Sdk -Version 0.3.0
                    
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="Meilynx.Sdk" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Meilynx.Sdk" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Meilynx.Sdk" />
                    
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 Meilynx.Sdk --version 0.3.0
                    
#r "nuget: Meilynx.Sdk, 0.3.0"
                    
#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 Meilynx.Sdk@0.3.0
                    
#: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=Meilynx.Sdk&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Meilynx.Sdk&version=0.3.0
                    
Install as a Cake Tool

Meilynx .NET SDK

CI NuGet License

Meilynx is an AI governance and FinOps platform that gives enterprises visibility and control over LLM usage — from cost and compliance to business outcomes. This SDK lets you send structured telemetry and outcome events from your .NET applications.

Install

dotnet add package Meilynx.Sdk

Quickstart

Wrap your AI calls for automatic telemetry — model, latency, and tokens are captured for you:

using Meilynx.Sdk;
using Meilynx.Sdk.Integrations;

var mx = new MeilynxClient(new MeilynxOptions
{
    ApiKey = "mx_live_...",  // BaseUrl defaults to https://api.meilynx.com
});

// Context propagation — all tracked calls inherit business metadata
await MeilynxContextScope.ObserveAsync(
    new ObserveOptions { FeatureKey = "search", CustomerId = "acme" },
    async () =>
    {
        // OpenAI
        var result = await OpenAiTracking.TrackAsync(mx, "gpt-4o", async () =>
        {
            return await chatClient.CompleteChatAsync(messages);
        });

        // Anthropic
        var response = await AnthropicTracking.TrackAsync(mx, "claude-sonnet-4-20250514", async () =>
        {
            return await anthropicClient.CreateMessageAsync(request);
        });
    });

await mx.ShutdownAsync();

Option 2: Explicit tracking

Full control over what you send:

using Meilynx.Sdk;

var mx = new MeilynxClient(new MeilynxOptions
{
    ApiKey = "mx_live_...",  // BaseUrl defaults to https://api.meilynx.com
});

mx.Track(new TelemetryEventInput
{
    EventType = "llm.response",
    CorrelationId = "run-123",
    CustomerId = "cust-acme",
    FeatureKey = "ask_docs",
    Model = "gpt-4o",
    Provider = "openai",
    PromptTokens = 1200,
    CompletionTokens = 220,
});

await mx.FlushAsync();
await mx.ShutdownAsync();

Which to choose? Use wrappers (Option 1) for most cases — they automatically capture model, latency, and agentic context with zero manual work. Use explicit tracking (Option 2) when you need custom event types, non-LLM operations, or providers without built-in integration.

Configuration

Environment variables (convention)

The SDK does not read environment variables directly. These are recommended names for your app configuration:

  • MX_BASE_URL (optional) — Meilynx API URL
  • MX_API_KEY — Project-scoped API key (mx_live_...)

Constructor options

Option Type Default Notes
BaseUrl string "https://api.meilynx.com" Base URL for the Meilynx API.
ApiKey string — Required. API key for /v1/ingest/*.
SourceSystem string "sdk" Source identifier.
FlushAt int 25 Batch size before flush.
FlushIntervalMs int 5000 Auto-flush interval.
MaxRetries int 3 Retry attempts on 429/5xx.
RetryDelayMs int 250 Base delay for backoff.
DisableValidation bool false Disable JSON schema validation.
HttpClient HttpClient? null Optional shared HttpClient instance.

Capturing outcomes

Outcomes are the business results your AI features produce:

using Meilynx.Sdk;

mx.CaptureOutcome(new OutcomeEventInput
{
    OutcomeType = "feature.result.accepted",
    IdempotencyKey = MeilynxIdempotency.MintIdempotencyKey("accepted", correlationId),
    CorrelationId = correlationId,
    CustomerId = "cust-acme",
    FeatureKey = "ask_docs",
    Attributes = new Dictionary<string, object?>
    {
        ["uiAction"] = "copy_clicked",
        ["timeToAcceptMs"] = 7000,
    },
});

Idempotency keys

Every outcome requires an IdempotencyKey to prevent duplicate processing. Use MeilynxIdempotency.MintIdempotencyKey() to generate a deterministic SHA-256 key from one or more fields:

using Meilynx.Sdk;

// Same inputs always produce the same key
MeilynxIdempotency.MintIdempotencyKey("accepted", "run-123");   // → "a1b2c3..."
MeilynxIdempotency.MintIdempotencyKey("accepted", "run-123");   // → "a1b2c3..." (same)
MeilynxIdempotency.MintIdempotencyKey("accepted", "run-456");   // → "d4e5f6..." (different)

Context propagation

The MeilynxContextScope.Observe / ObserveAsync methods propagate business context (correlation IDs, feature keys, customer IDs) through the async call stack using AsyncLocal<T>. All tracked AI calls inside inherit this context automatically:

MeilynxContextScope.Observe(
    new ObserveOptions { FeatureKey = "ask_docs", CustomerId = "acme" },
    () =>
    {
        // All tracked calls here are tagged with featureKey="ask_docs"
        var result = OpenAiTracking.Track(mx, "gpt-4o-mini", () =>
            chatClient.CompleteChat(messages));
    });

Nested scopes inherit the parent context and can override specific fields.

Agentic context

For agentic loops with multiple tool-call hops, set AgentName, ToolName, and StepIndex on each step:

for (int i = 0; i < steps.Count; i++)
{
    var step = steps[i];
    await MeilynxContextScope.ObserveAsync(
        new ObserveOptions { AgentName = "research-agent", ToolName = step.Name, StepIndex = i },
        async () =>
        {
            await OpenAiTracking.TrackAsync(mx, "gpt-4o", async () =>
                await chatClient.CompleteChatAsync(messages));
        });
}

The tracking helpers also capture ResponseToolCalls (tool names the model invoked) automatically from streaming and non-streaming responses.

Budget status

Check current budget utilization from your application. Results are cached for 60 seconds per query-parameter combination.

var status = await mx.GetBudgetStatusAsync(new BudgetStatusQuery { CustomerId = "acme" });

foreach (var budget in status.Budgets)
{
    if (budget.Action == "block")
        Console.WriteLine($"Budget {budget.Name} exceeded: {budget.UtilizationPct}%");
}

Failsafe behavior

The SDK is designed to never break your application. All tracking wrappers, context injection, and telemetry emission are wrapped in defensive error handling:

  • If telemetry emission fails, the error is swallowed silently.
  • Real LLM provider errors (rate limits, auth failures, invalid requests) always propagate normally.
  • Your Func<Task<T>> callback always executes, regardless of any SDK-internal failure.

In other words: a bug in the Meilynx SDK will never cause your AI calls to fail.

Streaming

The tracking helpers handle streaming transparently. For both OpenAI and Anthropic:

  • One telemetry event is emitted per completion (not per chunk)
  • IsStreaming is set to true automatically
  • LatencyMs measures time from request start to last token received
  • Tool calls in the response are accumulated into ResponseToolCalls
await foreach (var chunk in OpenAiTracking.TrackStreamAsync(mx, "gpt-4o", stream))
{
    // process chunk as normal — telemetry is emitted when the stream completes
}

Error handling

  • 429 and 5xx responses are retried with exponential backoff (max 4 seconds).
  • 401/403 errors throw UnauthorizedAccessException immediately.
  • Failed flushes re-enqueue events so they are not lost.

Batching and flushing

Events are buffered in memory and flushed automatically when the batch size (FlushAt) is reached or the flush interval elapses. Use FlushAsync() for deterministic delivery (e.g., before responding to an HTTP request) and ShutdownAsync() to drain queues before process exit.

Serverless and short-lived processes

In short-lived environments (Azure Functions, AWS Lambda via .NET), flush before the handler returns to avoid losing events:

// Azure Function / Minimal API endpoint
app.MapPost("/ask", async (AskRequest req) =>
{
    var result = await HandleRequest(req);
    await mx.FlushAsync();     // flush before returning
    return Results.Ok(result);
});

// Always call ShutdownAsync() when the host is stopping
lifetime.ApplicationStopping.Register(() => mx.ShutdownAsync().GetAwaiter().GetResult());

Set FlushIntervalMs = 0 to disable the background flush timer if the runtime does not support long-lived timers.

Compatibility

  • .NET 10+ (targets net10.0)
  • Thread-safe batching with background flush timer.
  • Not intended for client-side use. API keys must stay server-side.

Docs

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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
0.3.0 123 4/30/2026
0.2.3 112 4/23/2026
0.2.1 129 4/16/2026
0.2.0 118 4/15/2026
0.1.1 123 4/8/2026
0.1.0 116 4/8/2026