# .NET SDK

Use Enlitt from an ASP.NET Core backend or a Unity dedicated server.

The .NET SDK does what the Node.js SDK does, for an ASP.NET Core server or any
.NET 8 backend, such as a Unity dedicated server. It comes as three NuGet
packages:

| Package                | What it holds                                                                                             |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| `Enlitt`               | `EnlittSdk.WithPremiumChargeAsync`, `EnlittSdk.UsageAsync`, sessions, exceptions, and the login handlers. |
| `Enlitt.AspNetCore`    | `UseEnlittSession`, `MapEnlittAuth`, and `MapEnlittClientSignIn`.                                         |
| `Enlitt.Extensions.AI` | `EnlittChatClient`, a `Microsoft.Extensions.AI` `IChatClient` for standard and premium calls.             |

Every rule on the other pages applies unchanged. That includes
[login](/docs/authentication.md), [premium limits](/docs/premium-limits.md),
[idempotency](/docs/idempotency.md), and [errors](/docs/errors.md). This page shows
only the .NET names. Errors are exceptions, so `EnlittLoginRequiredError`
becomes `EnlittLoginRequiredException`.

## Install

```bash
dotnet add package Enlitt.AspNetCore
dotnet add package Enlitt.Extensions.AI
```

Then wire the project:

```bash
npx enlitt setup --name "My .NET app"
```

The CLI needs Node.js. `setup` finds your `.csproj` or `.sln`. It then writes
`ENLITT_CLIENT_ID` and `ENLITT_SECRET_KEY` to `.env` and records the
application in `enlitt.json`. Keep `.env` out of git, and load it into the
process environment (for example with your host's environment settings or a
`.env` loader). The SDK reads `ENLITT_SECRET_KEY` from the environment.

## Mount login and the session middleware

```csharp
using Enlitt;
using Enlitt.AspNetCore;
using Enlitt.Auth;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var config = new CallbackConfig
{
    ClientId = Environment.GetEnvironmentVariable("ENLITT_CLIENT_ID")!,
    AppOrigin = "https://your-app.example",
    RedirectUri = "https://your-app.example/enlitt/callback",
    DefaultReturnTo = "/",
    StateStore = new MyStateStore(), // implements IStateStore; share it across instances
    // SecretKey defaults to the ENLITT_SECRET_KEY environment variable.
};

app.UseEnlittSession();
app.MapEnlittAuth(config); // /enlitt/login, /enlitt/callback, /enlitt/logout
```

`MapEnlittAuth` takes optional path arguments. The callback path must be the
path of `RedirectUri`. The handlers behave exactly as described on the
[login page](/docs/authentication.md).

## Make a standard call

`EnlittChatClient.Standard()` is an `IChatClient`, so it works anywhere
`Microsoft.Extensions.AI` does:

```csharp
using Enlitt.Extensions.AI;
using Microsoft.Extensions.AI;

IChatClient standard = EnlittChatClient.Standard();

app.MapPost("/api/summary", async (CancellationToken ct) =>
{
    ChatResponse response = await standard.GetResponseAsync("Summarize this support ticket.", cancellationToken: ct);
    return Results.Json(new { text = response.Text });
});
```

`GetStreamingResponseAsync` streams. Cancel a call with the
`CancellationToken`. The standard model's limits are on the
[models page](/docs/models.md); unsupported `ChatOptions`, such as `Temperature`
or a required tool mode, throw `EnlittConfigurationException` before any
request is sent.

## Make a premium call

Wrap your own provider's `IChatClient`. Your provider key stays on your server:

```csharp
IChatClient premium = EnlittChatClient.Premium(myProviderChatClient);

var options = new ChatOptions
{
    AdditionalProperties = new()
    {
        ["enlitt"] = new EnlittProviderOptions
        {
            MaxCharge = "20000000", // NANOUSD, as a string
            Price = EnlittPricing.PerToken(input: "400", output: "1600"),
        },
    },
};

ChatResponse response = await premium.GetResponseAsync("Write a long story.", options, ct);
```

The provider and model labels come from the wrapped client's
`ChatClientMetadata`.

For any other provider (images, voice, another SDK), use
`WithPremiumChargeAsync`:

```csharp
byte[] image = await EnlittSdk.WithPremiumChargeAsync(
    new WithPremiumChargeOptions
    {
        MaxCharge = "5000000",
        Provider = new PremiumProvider("my-image-provider", "image-v2"),
    },
    async (charge, operation, ct) =>
    {
        byte[] result = await myImageProvider.GenerateAsync(prompt, ct);
        charge("5000000"); // what this call costs the user, in NANOUSD
        return result;
    },
    ct);
```

The call is reported exactly once. If the delegate returns, it is reported as
completed; if it throws, as failed; if its token is cancelled, as cancelled.

## Remaining capacity

```csharp
var capacity = await EnlittSdk.UsageAsync();
Console.WriteLine(capacity.Standard.RemainingPercent);
```

## Exceptions

The classes are:

* `EnlittException`
* `EnlittLoginRequiredException`
* `EnlittInsufficientCreditsException`
* `EnlittLimitExceededException`
* `EnlittSubscriptionInactiveException`
* `EnlittRateLimitException`
* `EnlittAuthenticationException`
* `EnlittAuthorizationException`
* `EnlittConfigurationException`

Each carries `Code`, `ProviderInvoked`, `Retryable`, `RetryAfterMs`,
`RequestId`, and `AuthorizeUrl`. The [errors page](/docs/errors.md) says which
ones are safe to show a user.

```csharp
try
{
    response = await standard.GetResponseAsync(prompt, cancellationToken: ct);
}
catch (EnlittLoginRequiredException)
{
    return Results.Redirect("/enlitt/login?return_to=/chat");
}
```

## Background jobs

Outside a request, wrap the work in a session you stored:

```csharp
await EnlittSdk.RunWithEnlittSessionAsync(new EnlittSession(storedToken), async () =>
{
    await standard.GetResponseAsync("Write tonight's digest.");
});
```

## Games and native apps

If your server backs a Unity game or a desktop or mobile app, add
`app.MapEnlittClientSignIn(...)` and
`app.UseEnlittSession(new EnlittSessionMiddlewareOptions { Header = true })`.
See [client sign-in](/docs/client-sign-in.md).
