Zonit.Extensions.Ai.Prompts
1.9.20
See the version list below for details.
dotnet add package Zonit.Extensions.Ai.Prompts --version 1.9.20
NuGet\Install-Package Zonit.Extensions.Ai.Prompts -Version 1.9.20
<PackageReference Include="Zonit.Extensions.Ai.Prompts" Version="1.9.20" />
<PackageVersion Include="Zonit.Extensions.Ai.Prompts" Version="1.9.20" />
<PackageReference Include="Zonit.Extensions.Ai.Prompts" />
paket add Zonit.Extensions.Ai.Prompts --version 1.9.20
#r "nuget: Zonit.Extensions.Ai.Prompts, 1.9.20"
#:package Zonit.Extensions.Ai.Prompts@1.9.20
#addin nuget:?package=Zonit.Extensions.Ai.Prompts&version=1.9.20
#tool nuget:?package=Zonit.Extensions.Ai.Prompts&version=1.9.20
Zonit.Extensions.Ai
A .NET library for integrating with multiple AI providers (OpenAI, Anthropic Claude, Google Gemini, X Grok, DeepSeek, Mistral) with Scriban templating, type-safe prompts, and built-in resilience.
NuGet Packages
| Package | Version | Downloads | Description |
|---|---|---|---|
| Zonit.Extensions.Ai | Core library with prompts and DI | ||
| Zonit.Extensions.Ai.Abstractions | Interfaces and contracts | ||
| Zonit.Extensions.Ai.OpenAi | OpenAI provider (GPT-5, O3/O4, DALL-E) | ||
| Zonit.Extensions.Ai.Anthropic | Anthropic provider (Claude 4.5) | ||
| Zonit.Extensions.Ai.Google | Google provider (Gemini 2.5/3) | ||
| Zonit.Extensions.Ai.X | X provider (Grok 4) | ||
| Zonit.Extensions.Ai.DeepSeek | DeepSeek provider (V3, R1) | ||
| Zonit.Extensions.Ai.Mistral | Mistral provider (Large, Codestral) | ||
| Zonit.Extensions.Ai.Prompts | Ready-to-use example prompts |
# Core library
dotnet add package Zonit.Extensions.Ai
# Add providers you need
dotnet add package Zonit.Extensions.Ai.OpenAi
dotnet add package Zonit.Extensions.Ai.Anthropic
dotnet add package Zonit.Extensions.Ai.Google
dotnet add package Zonit.Extensions.Ai.X
dotnet add package Zonit.Extensions.Ai.DeepSeek
dotnet add package Zonit.Extensions.Ai.Mistral
# Or via NuGet Package Manager
Install-Package Zonit.Extensions.Ai
Install-Package Zonit.Extensions.Ai.OpenAi
Features
- Multi-provider - OpenAI, Anthropic, Google, X, DeepSeek, Mistral with unified API
- Type-safe prompts - Strongly typed responses with JSON Schema
- Scriban templating - Dynamic prompts with variables and conditions
- Cost calculation - Estimate costs before calling API
- Resilience - Retry, circuit breaker, timeout with Microsoft.Extensions.Http.Resilience
- Plugin architecture - Auto-discovery of providers, idempotent registration with
TryAddEnumerable - Clean architecture - SOLID principles, each provider self-contained with own Options and DI
- Best practices -
BindConfiguration+PostConfigurepattern for configuration - Separation of concerns - Provider-specific options separated from global configuration
Requirements
- .NET 8.0, 9.0, or 10.0
Quick Start
1. Configuration-based (Recommended)
Register providers using appsettings.json configuration:
// appsettings.json
{
"Ai": {
"Resilience": {
"MaxRetryAttempts": 3,
"HttpClientTimeout": "00:05:00"
},
"OpenAi": {
"ApiKey": "sk-..."
},
"Anthropic": {
"ApiKey": "sk-ant-..."
},
"Google": {
"ApiKey": "AIza..."
},
"X": {
"ApiKey": "xai-..."
},
"DeepSeek": {
"ApiKey": "sk-..."
},
"Mistral": {
"ApiKey": "..."
}
}
}
// Program.cs - Configuration is automatically loaded via BindConfiguration
services.AddAiOpenAi(); // Loads from "Ai:OpenAi"
services.AddAiAnthropic(); // Loads from "Ai:Anthropic"
services.AddAiGoogle(); // Loads from "Ai:Google"
services.AddAiX(); // Loads from "Ai:X"
services.AddAiDeepSeek(); // Loads from "Ai:DeepSeek"
services.AddAiMistral(); // Loads from "Ai:Mistral"
2. Code-based Configuration
Override or supplement configuration in code:
// With API key only
services.AddAiOpenAi("sk-...");
// With full configuration (applied after appsettings.json via PostConfigure)
services.AddAiOpenAi(options =>
{
options.ApiKey = "sk-...";
options.OrganizationId = "org-...";
options.BaseUrl = "https://custom-endpoint.com";
});
// Multiple providers
services.AddAiOpenAi("sk-...");
services.AddAiAnthropic("sk-ant-...");
services.AddAiGoogle("AIza...");
3. Global Resilience Configuration
Configure retry/timeout behavior for all providers:
services.AddAi(options =>
{
options.Resilience.MaxRetryAttempts = 5;
options.Resilience.HttpClientTimeout = TimeSpan.FromMinutes(10);
options.Resilience.RetryBaseDelay = TimeSpan.FromSeconds(3);
});
// Then add providers
services.AddAiOpenAi();
services.AddAiAnthropic();
4. Plugin Architecture
Each provider registration is idempotent and can be called from multiple plugins:
// Plugin A
services.AddAiOpenAi(); // Registers OpenAI + core services
// Plugin B (safe - uses TryAddEnumerable internally)
services.AddAiAnthropic(); // Only adds Anthropic, doesn't duplicate core
// Plugin C
services.AddAi(options => // Safe - configures existing registration
{
options.Resilience.MaxRetryAttempts = 5;
});
Configuration Best Practices
✅ Recommended:
// appsettings.json for sensitive data (use User Secrets in dev)
services.AddAiOpenAi();
// Or override specific values
services.AddAiOpenAi(options =>
{
options.BaseUrl = "https://azure-openai.com"; // Override only what's needed
});
❌ Avoid:
// Hardcoding API keys
services.AddAiOpenAi("sk-hardcoded-key");
Creating Prompts
public class TranslatePrompt : PromptBase<TranslateResponse>
{
public required string Content { get; set; }
public required string Language { get; set; }
public override string Prompt => @"
Translate the following text into {{ language }}:
{{ content }}
";
}
[Description("Translation result")]
public class TranslateResponse
{
[Description("Translated text")]
public required string TranslatedText { get; set; }
[Description("Detected source language")]
public string? DetectedLanguage { get; set; }
}
// Usage
var result = await aiClient.GenerateAsync(
new GPT51(),
new TranslatePrompt { Content = "Hello!", Language = "Polish" }
);
Console.WriteLine(result.Value.TranslatedText); // "Cześć!"
Console.WriteLine(result.MetaData.PromptName); // "Translate" (auto-generated)
Cost Calculation
All results include a MetaData object with calculated costs using the Price value object:
var result = await aiClient.GenerateAsync(new GPT51(), prompt);
// Token usage (via MetaData)
Console.WriteLine($"Tokens: {result.MetaData.InputTokens} in / {result.MetaData.OutputTokens} out");
Console.WriteLine($"Total tokens: {result.MetaData.TotalTokens}");
Console.WriteLine($"Cached tokens: {result.MetaData.Usage.CachedTokens}");
// Cost breakdown (Price value object from Zonit.Extensions)
Console.WriteLine($"Input cost: {result.MetaData.InputCost}"); // e.g. 0.01
Console.WriteLine($"Output cost: {result.MetaData.OutputCost}"); // e.g. 0.03
Console.WriteLine($"Total cost: {result.MetaData.TotalCost}"); // e.g. 0.04
// Duration and request info
Console.WriteLine($"Duration: {result.MetaData.Duration.TotalSeconds:F2}s");
Console.WriteLine($"Model: {result.MetaData.Model}");
Console.WriteLine($"Provider: {result.MetaData.Provider}");
Console.WriteLine($"Request ID: {result.MetaData.RequestId}");
Result<T> Structure
// Result contains the value and metadata
public class Result<T>
{
public required T Value { get; init; }
public required MetaData MetaData { get; init; }
}
// MetaData contains all operation details
public class MetaData
{
public required ILlm Model { get; init; } // The model instance used
public required string Provider { get; init; } // "OpenAI", "Anthropic", etc.
public required string PromptName { get; init; } // Auto-generated from prompt class
public required TokenUsage Usage { get; init; }
public TimeSpan Duration { get; init; }
public string? RequestId { get; init; }
// Computed properties (shortcuts)
public int InputTokens => Usage.InputTokens;
public int OutputTokens => Usage.OutputTokens;
public int TotalTokens => Usage.TotalTokens;
public Price InputCost => Usage.InputCost;
public Price OutputCost => Usage.OutputCost;
public Price TotalCost => Usage.TotalCost;
}
Estimate costs before calling
Use IAiProvider methods to calculate or estimate costs:
// Calculate text generation cost
var cost = aiClient.CalculateCost(new GPT51(), inputTokens: 1000, outputTokens: 500);
Console.WriteLine($"Cost: {cost}");
// Calculate embedding cost
var embeddingCost = aiClient.CalculateCost(new TextEmbedding3Large(), inputTokens: 1000);
// Calculate image cost
var imageCost = aiClient.CalculateCost(new GPTImage1(), imageCount: 2);
// Calculate audio transcription cost (per minute)
var audioCost = aiClient.CalculateCost(new Whisper1(), durationSeconds: 180);
// Estimate cost from prompt text (estimates tokens automatically)
var estimated = aiClient.EstimateCost(new GPT51(), "Your prompt here...", estimatedOutputTokens: 500);
Simple API
The API is designed to be simple and intuitive. The same GenerateAsync method is used for all model types - the compiler resolves the correct overload based on the model interface:
// Text generation (ILlm)
var result = await aiClient.GenerateAsync(new GPT51(), "What is 2+2?");
Console.WriteLine(result.Value); // "4"
// Typed response with prompt class
var result = await aiClient.GenerateAsync(
new GPT51(),
new TranslatePrompt { Content = "Hello!", Language = "Polish" }
);
Console.WriteLine(result.Value.TranslatedText); // "Cześć!"
// Image generation (IImageLlm) - same method name, different model type
var image = await aiClient.GenerateAsync(new GPTImage1(), "A sunset over mountains");
if (image.IsSuccess)
await File.WriteAllBytesAsync("sunset.png", image.Value.Data);
// Embeddings (IEmbeddingLlm)
var embedding = await aiClient.GenerateAsync(new TextEmbedding3Large(), "Hello world");
float[] vector = embedding.Value;
// Audio transcription (IAudioLlm)
var audioBytes = await File.ReadAllBytesAsync("speech.mp3");
var audio = new Asset(audioBytes, "speech.mp3");
var transcription = await aiClient.GenerateAsync(new Whisper1(), audio, language: "en");
Console.WriteLine(transcription.Value);
// Streaming
await foreach (var chunk in aiClient.StreamAsync(new GPT51(), "Tell me a story"))
{
Console.Write(chunk);
}
Interface-based Type Detection
The model interface determines the operation:
| Interface | Method | Returns |
|---|---|---|
ILlm |
GenerateAsync(model, string) |
Result<string> |
ILlm |
GenerateAsync(model, IPrompt<T>) |
Result<T> |
IImageLlm |
GenerateAsync(model, string) |
Result<Asset> |
IEmbeddingLlm |
GenerateAsync(model, string) |
Result<float[]> |
IAudioLlm |
GenerateAsync(model, Asset) |
Result<string> |
Supported Models
OpenAI
| Model | Class | Price (in/out per 1M) | Features |
|---|---|---|---|
| GPT-5.2 | GPT52 |
$1.75 / $14.00 | Latest flagship, vision, tools |
| GPT-5.2 Pro | GPT52Pro |
$21.00 / $168.00 | Maximum quality |
| GPT-5.2 Codex | GPT52Codex |
$1.75 / $14.00 | Optimized for coding |
| GPT-5.1 | GPT51 |
$1.25 / $10.00 | Previous flagship |
| GPT-5.1 Pro | GPT51Pro |
$15.00 / $120.00 | Premium GPT-5.1 |
| GPT-5.1 Codex | GPT51Codex |
$1.25 / $10.00 | Coding agent |
| GPT-5 | GPT5 |
$1.25 / $10.00 | Base GPT-5 |
| GPT-5 Pro | GPT5Pro |
$15.00 / $120.00 | Premium GPT-5 |
| GPT-5 Mini | GPT5Mini |
$0.25 / $2.00 | Cost-effective |
| GPT-5 Nano | GPT5Nano |
$0.05 / $0.40 | Ultra-cheap |
| GPT-4.1 | GPT41 |
$2.00 / $8.00 | Latest GPT-4 |
| GPT-4.1 Mini | GPT41Mini |
$0.40 / $1.60 | Fast, affordable |
| GPT-4.1 Nano | GPT41Nano |
$0.10 / $0.40 | Cheapest GPT-4 |
| GPT-4o | GPT4o |
$2.50 / $10.00 | Multimodal |
| GPT-4o Mini | GPT4oMini |
$0.15 / $0.60 | Fast multimodal |
| O3 | O3 |
$2.00 / $8.00 | Reasoning model |
| O3 Pro | O3Pro |
$20.00 / $80.00 | Premium reasoning |
| O3 Mini | O3Mini |
$1.10 / $4.40 | Cost-effective reasoning |
| O4 Mini | O4Mini |
$1.10 / $4.40 | Latest mini reasoning |
| O1 | O1 |
$15.00 / $60.00 | Previous O-series |
| O3 Deep Research | O3DeepResearch |
$10.00 / $40.00 | Advanced research |
| GPT Image 1.5 | GPTImage15 |
Per image | Image generation |
| GPT Image 1 | GPTImage1 |
Per image | Image generation |
| GPT Image 1 Mini | GPTImage1Mini |
Per image | Cost-effective images |
| Text Embedding 3 Large | TextEmbedding3Large |
$0.13 / - | 3072 dimensions |
| Text Embedding 3 Small | TextEmbedding3Small |
$0.02 / - | 1536 dimensions |
| Whisper | Whisper1 |
$0.006/min | Audio transcription |
| GPT-4o Transcribe | GPT4oTranscribe |
$0.006/min | Audio transcription |
Anthropic (Claude)
| Model | Class | Price (in/out per 1M) | Features |
|---|---|---|---|
| Claude Sonnet 4.5 | Sonnet45 |
$3.00 / $15.00 | Best balance, agents, coding |
| Claude Opus 4.5 | Opus45 |
$5.00 / $25.00 | Maximum intelligence |
| Claude Haiku 4.5 | Haiku45 |
$1.00 / $5.00 | Fastest, cost-effective |
| Claude Sonnet 4 | Sonnet4 |
$3.00 / $15.00 | Previous Sonnet |
| Claude Opus 4 | Opus4 |
$5.00 / $25.00 | Previous Opus |
| Claude Sonnet 3.5 | Sonnet35 |
$3.00 / $15.00 | Legacy Sonnet |
| Claude Haiku 3.5 | Haiku35 |
$0.80 / $4.00 | Legacy Haiku |
Google (Gemini)
| Model | Class | Features |
|---|---|---|
| Gemini 2.5 Pro | Gemini25Pro |
Most capable thinking model |
| Gemini 2.5 Flash | Gemini25Flash |
Best price-to-performance |
| Gemini 2.5 Flash Lite | Gemini25FlashLite |
Ultra fast, low cost |
| Gemini 2.0 Flash | Gemini20Flash |
Second-gen flash |
| Gemini 2.0 Flash Lite | Gemini20FlashLite |
Cost-effective |
| Text Embedding 004 | TextEmbedding004 |
Embeddings |
X (Grok)
| Model | Class | Features |
|---|---|---|
| Grok-4 | Grok4 |
Latest Grok, web search |
| Grok-4.1 Fast | Grok41Fast |
Advanced reasoning |
| Grok-4.1 Fast Reasoning | Grok41FastReasoning |
Full reasoning enabled |
| Grok-4.1 Fast Non-Reasoning | Grok41FastNonReasoning |
High-speed, no reasoning overhead |
| Grok-3 | Grok3 |
Previous generation |
| Grok-3 Fast | Grok3Fast |
Fast Grok-3 |
| Grok-3 Mini | Grok3Mini |
Cost-effective |
DeepSeek
| Model | Class | Price (in/out per 1M) | Features |
|---|---|---|---|
| DeepSeek V3 | DeepSeekV3 |
$0.28 / $0.42 | General-purpose, 128K context |
| DeepSeek R1 | DeepSeekR1 |
$0.28 / $0.42 | Reasoning with thinking mode |
| DeepSeek Coder V3 | DeepSeekCoderV3 |
$0.28 / $0.42 | Optimized for coding |
Mistral
| Model | Class | Price (in/out per 1M) | Features |
|---|---|---|---|
| Mistral Large | MistralLarge |
$2.00 / $6.00 | Most capable, multimodal |
| Mistral Medium | MistralMedium |
$0.40 / $2.00 | Balanced |
| Mistral Small | MistralSmall |
$0.10 / $0.30 | Fast, cost-effective |
| Codestral | Codestral |
$0.30 / $0.90 | Optimized for code |
| Mistral Embed | MistralEmbed |
$0.10 / - | Embeddings |
Scriban Templating
Properties are automatically available as snake_case:
public class EmailPrompt : PromptBase<EmailResponse>
{
public string RecipientName { get; set; } // {{ recipient_name }}
public List<string> Topics { get; set; } // {{ topics }}
public bool IsFormal { get; set; } // {{ is_formal }}
public override string Prompt => @"
Write an email to {{ recipient_name }}.
{{~ if is_formal ~}}
Use formal tone.
{{~ end ~}}
Topics:
{{~ for topic in topics ~}}
- {{ topic }}
{{~ end ~}}
";
}
Files and Images
The library uses Asset from Zonit.Extensions for file handling. Asset is a versatile value object that:
- Auto-detects MIME type from binary data
- Provides
IsImage,IsDocument,IsAudioproperties - Includes
ToBase64(),ToDataUrl()for encoding - Has implicit conversions from
byte[]andStream
public class AnalyzePrompt : PromptBase<AnalysisResult>
{
public override string Prompt => "Analyze the documents";
}
// From file path
using var httpClient = new HttpClient();
var imageBytes = await httpClient.GetByteArrayAsync("https://example.com/image.jpg");
var asset = new Asset(imageBytes, "image.jpg");
// Or from local file
var localBytes = await File.ReadAllBytesAsync("document.pdf");
var pdfAsset = new Asset(localBytes, "document.pdf");
var result = await aiClient.GenerateAsync(
new GPT51(),
new AnalyzePrompt { Files = [asset] }
);
Image Generation
Using ImagePromptBase
Each image model (GPTImage1, GPTImage1Mini, GPTImage15) defines its own QualityType and SizeType enums with [EnumValue] attributes that map directly to API values. This ensures correct API parameters per model.
// Simple usage with model-specific enums (required)
var result = await aiClient.GenerateAsync(
new GPTImage1 { Quality = GPTImage1.QualityType.High, Size = GPTImage1.SizeType.Landscape },
new ImagePromptBase("A sunset over mountains with dramatic clouds")
);
if (result.IsSuccess)
await File.WriteAllBytesAsync("sunset.png", result.Value.Data);
// Custom image prompt with additional context
public class ProductImagePrompt : ImagePromptBase
{
public ProductImagePrompt(string productName, string style)
: base($"Professional product photo of {productName} in {style} style, white background, studio lighting")
{
}
}
var productImage = await aiClient.GenerateAsync(
new GPTImage1 { Quality = GPTImage1.QualityType.High, Size = GPTImage1.SizeType.Square },
new ProductImagePrompt("wireless headphones", "minimalist")
);
Image Quality and Size Options
Each model has its own QualityType and SizeType enums. Quality and Size are required parameters.
// GPT Image 1 - supports Auto, Low, Medium, High quality and Auto, Square, Landscape, Portrait sizes
var result = await aiClient.GenerateAsync(
new GPTImage1
{
Quality = GPTImage1.QualityType.High, // Auto, Low, Medium, High
Size = GPTImage1.SizeType.Landscape // Auto, Square, Landscape, Portrait
},
"A beautiful landscape"
);
// GPT Image 1 Mini - no Auto options (must be explicit)
var miniResult = await aiClient.GenerateAsync(
new GPTImage1Mini
{
Quality = GPTImage1Mini.QualityType.Medium, // Low, Medium, High (no Auto)
Size = GPTImage1Mini.SizeType.Square // Square, Portrait, Landscape (no Auto)
},
"A simple icon"
);
// Get image generation price based on quality and size
var model = new GPTImage1 { Quality = GPTImage1.QualityType.High, Size = GPTImage1.SizeType.Landscape };
var pricePerImage = model.GetImageGenerationPrice(); // Returns $0.25
Different Image Models
// GPT Image 1 - Full featured with Auto support
var image1 = await aiClient.GenerateAsync(
new GPTImage1 { Quality = GPTImage1.QualityType.High, Size = GPTImage1.SizeType.Landscape },
"A detailed architectural rendering"
);
// GPT Image 1 Mini - Cost-effective (cheapest option)
var imageMini = await aiClient.GenerateAsync(
new GPTImage1Mini { Quality = GPTImage1Mini.QualityType.Low, Size = GPTImage1Mini.SizeType.Square },
"A simple icon design"
);
// GPT Image 1.5 - Latest model with best quality
var image15 = await aiClient.GenerateAsync(
new GPTImage15 { Quality = GPTImage15.QualityType.High, Size = GPTImage15.SizeType.Auto },
"A photorealistic portrait"
);
Image Pricing (per image)
| Model | Quality | Square (1024x1024) | Landscape/Portrait |
|---|---|---|---|
| GPT Image 1 | Low | $0.011 | $0.016 |
| GPT Image 1 | Medium | $0.042 | $0.063 |
| GPT Image 1 | High | $0.167 | $0.250 |
| GPT Image 1 Mini | Low | $0.005 | $0.006 |
| GPT Image 1 Mini | Medium | $0.011 | $0.015 |
| GPT Image 1 Mini | High | $0.036 | $0.052 |
Reasoning Models
GPT-5 series and O-series models support reasoning with configurable effort:
// Configure reasoning effort
var result = await aiClient.GenerateAsync(
new GPT52
{
Reason = ReasoningEffort.High, // None, Low, Medium, High
ReasonSummary = ReasoningSummary.Auto, // None, Auto, Detailed
OutputVerbosity = Verbosity.Medium // Low, Medium, High
},
new ComplexAnalysisPrompt { Data = complexData }
);
// O-series models (always reasoning)
var o3Result = await aiClient.GenerateAsync(
new O3 { Reason = ReasoningEffort.High },
"Solve this complex mathematical proof..."
);
// Legacy nested types (deprecated but supported)
var legacyResult = await aiClient.GenerateAsync(
new GPT51
{
Reason = (ReasoningEffort)OpenAiReasoningBase.ReasonType.High,
OutputVerbosity = (Verbosity)OpenAiReasoningBase.VerbosityType.Medium
},
prompt
);
Backward Compatibility
MetaData Constructor
For legacy code using the old constructor syntax:
// Old syntax (deprecated but still works)
#pragma warning disable CS0618
var metadata = new MetaData(new GPT51(), new Usage { Input = 500, Output = 30 });
#pragma warning restore CS0618
// New recommended syntax
var metadata = new MetaData
{
Model = new GPT51(),
Usage = new TokenUsage { InputTokens = 500, OutputTokens = 30 },
Provider = "OpenAI",
PromptName = "MyPrompt"
};
Usage to TokenUsage
// Old Usage class (deprecated)
#pragma warning disable CS0618
var usage = new Usage { Input = 1000, Output = 500 };
TokenUsage tokenUsage = usage; // Implicit conversion
#pragma warning restore CS0618
// New TokenUsage class (recommended)
var tokenUsage = new TokenUsage
{
InputTokens = 1000,
OutputTokens = 500,
CachedTokens = 200,
ReasoningTokens = 100
};
ImagePrompt to ImagePromptBase
// Old ImagePrompt (deprecated)
#pragma warning disable CS0618
var oldPrompt = new ImagePrompt("A sunset");
#pragma warning restore CS0618
// New ImagePromptBase (recommended)
var newPrompt = new ImagePromptBase("A sunset");
Resilience Configuration
The library uses Microsoft.Extensions.Http.Resilience with optimized settings for AI providers. Configure retry, timeout, and circuit breaker behavior globally:
{
"Ai": {
"Resilience": {
"TotalRequestTimeout": "00:40:00", // 40 minutes total (including retries)
"AttemptTimeout": "00:10:00", // 10 minutes per attempt
"MaxRetryAttempts": 3, // Retry up to 3 times
"RetryBaseDelay": "00:00:02", // Start with 2s delay
"RetryMaxDelay": "00:00:30", // Max 30s delay
"UseJitter": true, // Add random jitter to prevent thundering herd
"CircuitBreakerSamplingDuration": "00:05:00",
"CircuitBreakerFailureRatio": 0.5,
"CircuitBreakerMinimumThroughput": 5,
"CircuitBreakerBreakDuration": "00:00:30"
}
}
}
Or configure in code:
services.AddAi(options =>
{
options.Resilience.TotalRequestTimeout = TimeSpan.FromMinutes(15);
options.Resilience.AttemptTimeout = TimeSpan.FromMinutes(10);
options.Resilience.MaxRetryAttempts = 5;
options.Resilience.RetryBaseDelay = TimeSpan.FromSeconds(3);
options.Resilience.RetryMaxDelay = TimeSpan.FromMinutes(1);
options.Resilience.UseJitter = true;
});
Default Resilience Settings
| Setting | Default | Description |
|---|---|---|
TotalRequestTimeout |
40 min | Maximum time for entire request pipeline (including retries) |
AttemptTimeout |
10 min | Timeout for single request attempt |
MaxRetryAttempts |
3 | Number of retry attempts on transient failures |
RetryBaseDelay |
2s | Initial delay between retries (exponential backoff) |
RetryMaxDelay |
30s | Maximum delay between retries |
UseJitter |
true | Add randomness to delays to prevent thundering herd |
CircuitBreakerFailureRatio |
0.5 | 50% failure rate opens circuit |
CircuitBreakerBreakDuration |
30s | Time circuit stays open before test |
Retry Policy
Requests are automatically retried on:
- Network errors (
HttpRequestException) - Timeouts (
TaskCanceledException,TimeoutException) - Rate limiting (HTTP 429)
- Server errors (HTTP 500, 502, 503, 504)
Per-Provider Timeout Override
Each provider can override the global timeout:
{
"Ai": {
"Resilience": {
"TotalRequestTimeout": "00:40:00" // Default for all
},
"OpenAi": {
"Timeout": "01:00:00" // Override for OpenAI only
}
}
}
services.AddAiOpenAi(options =>
{
options.Timeout = TimeSpan.FromMinutes(15); // OpenAI-specific
});
Example Prompts
The Zonit.Extensions.Ai.Prompts package includes ready-to-use prompts:
// Translation
var result = await ai.GenerateAsync(new GPT51(),
new TranslatePrompt { Content = "Hello", Language = "Polish" });
// Sentiment analysis
var result = await ai.GenerateAsync(new GPT51(),
new SentimentPrompt { Content = "I love this product!" });
// Summarization
var result = await ai.GenerateAsync(new GPT51(),
new SummarizePrompt { Content = longText, MaxWords = 100 });
// Code generation
var result = await ai.GenerateAsync(new GPT51(),
new CodePrompt { Description = "Fibonacci function", Language = "C#" });
// Classification
var result = await ai.GenerateAsync(new GPT51(),
new ClassifyPrompt { Content = text, Categories = ["Tech", "Sports", "News"] });
Architecture
The library follows SOLID principles and Clean Architecture:
Project Structure
Zonit.Extensions.Ai/
├── Source/
│ ├── Zonit.Extensions.Ai/ # Core library
│ │ ├── AiOptions.cs # Global options ("Ai" section)
│ │ ├── AiServiceCollectionExtensions # AddAi() registration
│ │ └── AiProvider.cs # Main provider orchestrator
│ │
│ ├── Zonit.Extensions.Ai.Abstractions/ # Interfaces only
│ │ ├── IPrompt.cs, ILlm.cs # Contracts
│ │ └── IAiProvider.cs # Provider interface
│ │
│ ├── Zonit.Extensions.Ai.OpenAi/ # OpenAI provider
│ ├── Zonit.Extensions.Ai.Anthropic/ # Anthropic provider
│ ├── Zonit.Extensions.Ai.Google/ # Google provider
│ ├── Zonit.Extensions.Ai.X/ # X provider
│ ├── Zonit.Extensions.Ai.DeepSeek/ # DeepSeek provider
│ ├── Zonit.Extensions.Ai.Mistral/ # Mistral provider
│ │
│ └── Zonit.Extensions.Ai.Prompts/ # Ready-to-use prompts
Key Design Principles
Separation of Concerns
- Each provider is a separate NuGet package
- Provider-specific options are in the provider package
- Global options (
AiOptions) contain only shared configuration
Dependency Inversion
- Providers implement
IModelProviderfrom Abstractions - Core library depends only on abstractions
- Providers discovered via auto-discovery or explicit registration
- Providers implement
Open/Closed Principle
- Add new providers without modifying core library
- Extend via
IModelProviderinterface
Configuration Pattern
// Best practice: BindConfiguration + PostConfigure services.AddOptions<OpenAiOptions>() .BindConfiguration("Ai:OpenAi"); // Load from appsettings.json if (options is not null) services.PostConfigure(options); // Override with codeIdempotent Registration
- Uses
TryAddSingletonandTryAddEnumerable - Safe to call from multiple plugins
- No duplicate registrations
- Uses
Configuration Hierarchy
{
"Ai": { // Global (AiOptions)
"Resilience": { ... }, // Shared by all providers
"OpenAi": { ... }, // OpenAiOptions (provider-specific)
"Anthropic": { ... }, // AnthropicOptions
"Google": { ... }, // GoogleOptions
"X": { ... }, // XOptions
"DeepSeek": { ... }, // DeepSeekOptions
"Mistral": { ... } // MistralOptions
}
}
Provider Options Inheritance
// Base class for all provider options
public abstract class AiProviderOptions
{
public string? ApiKey { get; set; }
public string? BaseUrl { get; set; }
public TimeSpan? Timeout { get; set; }
}
// Each provider extends the base
public sealed class OpenAiOptions : AiProviderOptions
{
public const string SectionName = "Ai:OpenAi";
public string? OrganizationId { get; set; } // OpenAI-specific
}
public sealed class AnthropicOptions : AiProviderOptions
{
public const string SectionName = "Ai:Anthropic";
// Anthropic-specific properties can be added here
}
Dependency Injection Flow
User Code
↓
services.AddAiOpenAi()
↓
├─ services.AddAi() # Core services (idempotent)
│ ├─ Register IAiProvider
│ ├─ Configure AiOptions
│ └─ Auto-discover providers
│
├─ Configure OpenAiOptions # Provider-specific
│ └─ BindConfiguration("Ai:OpenAi")
│
├─ Register OpenAiProvider # Provider implementation
│ └─ TryAddEnumerable (no duplicates)
│
└─ AddHttpClient<OpenAiProvider>() # Resilience
└─ AddStandardResilienceHandler()
AOT and Trimming
This library uses reflection for:
- Scriban templating (property discovery)
- JSON serialization (response parsing)
- Provider auto-discovery (assembly scanning)
For AOT projects, use Source Generators:
AiJsonSerializerGenerator- Generates JsonSerializerContextAiProviderRegistrationGenerator- Generates provider registration
License
MIT License
| Product | Versions 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 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. |
-
net10.0
- Zonit.Extensions.Ai (>= 1.9.20)
-
net8.0
- Zonit.Extensions.Ai (>= 1.9.20)
-
net9.0
- Zonit.Extensions.Ai (>= 1.9.20)
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.20.0 | 109 | 1/31/2026 |
| 1.13.0 | 104 | 1/25/2026 |
| 1.12.0 | 99 | 1/22/2026 |
| 1.11.0 | 104 | 1/22/2026 |
| 1.10.0 | 103 | 1/22/2026 |
| 1.9.20 | 109 | 1/22/2026 |
| 1.9.19 | 107 | 1/21/2026 |
| 1.9.18 | 102 | 1/21/2026 |
| 1.9.17 | 100 | 1/21/2026 |
| 1.9.16 | 93 | 1/21/2026 |
| 1.9.15 | 97 | 1/21/2026 |
| 1.9.14 | 105 | 1/20/2026 |
| 1.9.12 | 94 | 1/20/2026 |
| 1.9.11 | 95 | 1/20/2026 |
| 1.9.10 | 96 | 1/20/2026 |
| 1.9.9 | 96 | 1/20/2026 |
| 1.9.8 | 94 | 1/20/2026 |
| 1.9.7 | 88 | 1/20/2026 |
| 1.9.6 | 94 | 1/20/2026 |
| 1.9.5 | 93 | 1/20/2026 |