Configuration and dependency injection
There are two ways to make an IntentClient. The builder, IntentClientBuilder, works in any program.
AddIntentSharp registers the client with a service collection, for ASP.NET Core, worker services and other
applications on the .NET host, and reads its settings from configuration. Both make the same client: it holds the
providers, the options, the memory, the request cache and the rate limiter, and it checks the license key once,
when it is built.
Make one client per application and share it. There is no process-wide default client: pass the client, inject
it, or make it ambient for one async flow with AsAmbient(), as the quick start shows.
In code, with the builder
var client = new IntentClientBuilder()
.UseProvidersFromEnvironment()
.Configure(o =>
{
o.MaxAttempts = 3;
o.MaxInputChars = 50_000;
o.DefaultTimeout = TimeSpan.FromSeconds(30);
o.JudgeMode = JudgeMode.Never;
})
.WithCache(new MemoryRequestCache(capacity: 500))
.WithRateLimiter(new TokenBucketRateLimiter(requestsPerSecond: 5, burst: 10))
.WithMemory(null)
.Build();
- Providers. Add a primary provider before
Build:UseProvidersFromEnvironment,UseAnthropic,UseOpenAI,UseProviderfor one of your own, orUseChatClientfromIntentSharp.Extensions.AI. The...AsFallbackmethods andUseFallbackProvideradd the provider used when the primary fails with a transient error. Providers covers each one. - Options.
Configurechanges the client options at once; several calls apply in order.Buildchecks every value and throwsArgumentOutOfRangeException, naming the setting and its range, for one that is out of range. The options are listed below. - Logging. Call
WithLoggerFactorybefore the provider methods, which create their providers at once. Without it nothing is logged. Logging and telemetry lists what is logged. - The rest.
WithCache,WithRateLimiter,WithMemory,WithJudge,WithHttpClientFactoryandWithLicenseKey. Their defaults are in the next table.
What a client built with the builder has unless you change it:
| Part | Default |
|---|---|
| Judge | A model judge on the primary provider, which runs only when JudgeMode lets it. |
| License key | INTENTSHARP_LICENSE_KEY from the environment, when no key is set in code. |
The memory, the request cache and the rate limiter, with their defaults on the builder and under AddIntentSharp,
are in Memory, cache and rate limiting.
From configuration, with AddIntentSharp
AddIntentSharp is in the IntentSharp.DependencyInjection namespace. It reads the IntentSharp section of the
application's configuration and registers the client as a singleton:
// In ASP.NET Core: builder.Services.AddIntentSharp(builder.Configuration);
services.AddIntentSharp(configuration);
A section in appsettings.json with every value at its default looks like this. Leave out any value you do not
change:
{
"IntentSharp": {
"EnableRequestCache": true,
"CacheCapacity": 1000,
"RateLimitPerSecond": 0,
"RateLimitBurst": 10,
"Options": {
"FailWhenUnlicensed": false,
"LicenseReminderInterval": "01:00:00",
"MaxAttempts": 2,
"MaxOutputTokens": 4096,
"MaxInputChars": 200000,
"MaxModelCalls": 6,
"JudgeMode": "OnRequest",
"RecordInMemory": true,
"UseCache": true,
"CacheTtl": "01:00:00",
"FallbackOnTransientErrors": true
}
}
}
Each value can also come from an environment variable, with __ in place of :, for example
IntentSharp__Options__MaxAttempts. ASP.NET Core and the .NET host read environment variables into the
configuration by default. To read another section, pass its name: AddIntentSharp(configuration, "Ai").
Or set the values in code, with the same names:
services.AddIntentSharp(settings =>
{
settings.PrimaryProvider = "OpenAI";
settings.OpenAIModel = "gpt-4.1";
settings.MemoryPath = ""; // no memory
settings.RateLimitPerSecond = 5;
settings.Options.MaxAttempts = 3;
settings.Options.DefaultTimeout = TimeSpan.FromSeconds(30);
});
The IntentSharp section
| Setting | Default | What it does |
|---|---|---|
AnthropicApiKey |
The ANTHROPIC_API_KEY environment variable |
The Anthropic key. Blank leaves Anthropic out. |
OpenAIApiKey |
The OPENAI_API_KEY environment variable |
The OpenAI key. Blank leaves OpenAI out. |
AnthropicModel, OpenAIModel |
The provider's default model | The model used when a request names none. |
PrimaryProvider |
The provider whose key is set; Anthropic when both are | Anthropic or OpenAI, ignoring case. With both keys set, the other provider is the fallback. Any other name, a primary without its key, or no key at all throws InvalidOperationException when the client is built, so a misspelt name never picks a provider silently. |
MemoryPath |
Not set: an in-process memory | A file path keeps the memory in a SQLite file. An empty string turns memory off. |
EnableRequestCache |
true |
Serve identical requests from a request cache: one you register as IRequestCache, or an in-process cache of CacheCapacity entries. |
CacheCapacity |
1000 |
The entries the in-process cache holds before it drops the least recently used one. |
RateLimitPerSecond |
0 (off) |
Requests per second per provider for a rate limiter inside the process. |
RateLimitBurst |
10 |
The requests a provider may make at once before the rate limiter holds it to RateLimitPerSecond. |
Options |
The client options, in the next table. |
An ISemanticMemory, IRequestCache or IRateLimiter you register in the container is used in place of the one
these settings would create, while the matching setting is on. AddIntentSharp adds an in-process request cache
unless EnableRequestCache is false; the builder adds none unless you pass one.
The client options
These are the Options subsection, and the properties of IntentClientOptions that Configure sets on the
builder.
| Option | Default | What it does |
|---|---|---|
MaxAttempts |
2 |
Attempts per request. Attempt 2 and later carry the findings of the attempt before. |
MaxModelCalls |
6 |
The most model calls one request may make, judge calls included. |
MaxOutputTokens |
4096 |
The output token ceiling of each model call. |
Temperature |
Not set | The sampling temperature, from 0 to 2, sent to providers that accept one. |
MaxInputChars |
200000 |
The longest serialized input, in characters. A longer input throws ArgumentException before any model call; it is never cut short. |
DefaultTimeout |
Not set | The time one request may take, every attempt included, for example "00:00:30". A request that runs out of time fails with an issue. |
JudgeMode |
OnRequest |
When the model judge runs: Never, OnRequest (for requests that set UseJudge) or Always. |
JudgeModel |
Not set | The model for judge calls; the provider's default when not set. |
RecordInMemory |
true |
Record each completed request in the memory. |
UseCache |
true |
Serve identical requests from the request cache, when the client has one. |
CacheTtl |
01:00:00 |
How long a cached reply is kept. |
FallbackOnTransientErrors |
true |
Send a request to the fallback provider when the primary fails with a transient error. |
LicenseKey |
Not set | Your license key. Your license key shows where to keep it. |
FailWhenUnlicensed |
false |
When true, building the client throws LicenseException if no key is set, the key is not valid or the evaluation has ended, instead of logging a notice. |
LicenseReminderInterval |
01:00:00 |
The least time between repeated license notices while the client is used. |
License behaviour describes the license notices. MaxAttempts,
MaxOutputTokens, Temperature, MaxModelCalls and the timeout (as Timeout) can also be set for one request, on
AIRequestOptions; a value set there must be in the same range, or the call throws ArgumentOutOfRangeException
before any model call.
The client is built when the host starts
Under the .NET host, AddIntentSharp builds the client while the host starts, before it starts hosted services or
serves a request. An exception from building it (a missing provider key, an unknown PrimaryProvider, an option
out of range, or a LicenseException) is thrown from the host's start, unwrapped, rather than from the first
request. The license notice for the key is logged at startup.
The client is built once. A change to the configuration while the application runs takes effect at the next start.
Without the host, resolve the client once at startup for the same effect:
services.AddIntentSharp(configuration);
using var provider = services.BuildServiceProvider();
// Without a .NET host, resolve the client once at startup: a missing provider key, a value out of range
// or a LicenseException then throws here, not on the first request.
var client = provider.GetRequiredService<IntentClient>();
Injecting the client
Take IntentClient as a constructor parameter, like any other singleton:
/// <summary>A service that receives the client from the container, like any other dependency.</summary>
public sealed class OrderTriage(IntentClient client)
{
public async Task<int?> UrgencyAsync(string email, CancellationToken cancellationToken)
{
var result = await client.TransformAsync<int>(email,
"Rate the urgency of this order email from 1 (routine) to 5 (critical).",
cancellationToken: cancellationToken);
return result.Success ? result.Value : null;
}
}
The container disposes the client when it is disposed, and the client disposes its providers.
The HTTP clients
The OpenAI and Anthropic providers that AddIntentSharp creates take their HttpClient from
IHttpClientFactory, under the names in ServiceCollectionExtensions.OpenAIHttpClientName and
ServiceCollectionExtensions.AnthropicHttpClientName. Configure those names to add a proxy, a handler or headers:
services.AddIntentSharp(configuration);
services.AddHttpClient(ServiceCollectionExtensions.AnthropicHttpClientName)
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
Proxy = new WebProxy("http://proxy.example.internal:8080"),
UseProxy = true
});
Each attempt still has the provider's own time limit, 120 seconds by default (Providers).
A provider AddIntentSharp does not create
AddIntentSharp creates the OpenAI and Anthropic providers only. For an IChatClient, or a provider of your own,
register a client you build yourself in place of calling AddIntentSharp, and resolve it once at startup:
// A provider AddIntentSharp does not create: here an IChatClient the application registers itself.
services.AddSingleton(sp => new IntentClientBuilder()
.WithLoggerFactory(sp.GetRequiredService<ILoggerFactory>())
.UseChatClient(sp.GetRequiredService<IChatClient>(), name: "AzureOpenAI")
.Build());
Intent files in the container
IntentSharp.Catalog adds AddIntentCatalog and AddIntentRuntime, which register a folder of intent files and
the runtime that runs them, on top of the client. Running intents from files covers them.