| name | cisharpai-expert |
| description | Expert guidance for the Cisharpai .NET library โ a unified HttpClient-based interface for OpenAI, Azure OpenAI, Azure AI Inference, Anthropic, and Cohere LLM providers. Use when writing, debugging, or architecting code that uses Cisharpai clients, features, DTOs, DI registration, or provider-specific integrations. Activates on mentions of "Cisharpai", "IChatCompletionClient", "IEmbeddingClient", provider setup, tool calling, streaming, JSON output, grounded chat, vision, embeddings, or fake clients for testing.
|
Cisharpai Expert
Overview
Cisharpai is a unified .NET client library providing a common HttpClient-based
interface for multiple LLM providers. Switching providers is a configuration
change โ application code stays the same.
Supported Providers: OpenAI, Azure OpenAI, Azure AI Inference, Anthropic, Cohere
Key Design Principles:
- Unified Abstraction โ Same
IChatCompletionClient / IEmbeddingClient for all providers
- No Exceptions for API Errors โ
IsSuccess + ErrorMessage on responses; exceptions only for network/config
- Debuggability โ
RawResponseJson / RawRequestJson on every response
- Immutable DTOs โ Request/Response types are immutable records
- Feature Collection Pattern โ Optional capabilities via
IHasFeatures.Features.Get<T>()
- Escape Hatch โ
ExtraParameters deep-merges arbitrary JSON into requests
- HTTP Resilience โ DI helpers add retry, timeout, and circuit-breaker policies;
Create(...) requires explicit named HttpClient resilience registration
Quick Start
1. Install Packages
<PackageReference Include="Cisharpai" />
<PackageReference Include="Cisharpai.OpenAi" />
2. Register via DI
services.AddOpenAiClient(o => { o.ApiKey = "sk-..."; });
services.AddAzureOpenAiClient(o => {
o.Endpoint = "https://myresource.openai.azure.com";
o.DeploymentName = "gpt-4o";
o.ApiKey = "...";
});
services.AddAnthropicClient(o => { o.ApiKey = "sk-ant-..."; });
services.AddCohereChatClient(o => { o.ApiKey = "..."; });
3. Send a Request
using Cisharpai.Models;
var request = new ChatCompletionRequest(
Messages: [new LlmMessage(LlmRole.User, "Hello!")],
Model: "gpt-4o");
var response = await client.GetChatCompletionAsync(request);
if (response.IsSuccess)
Console.WriteLine(response.Content);
else
Console.WriteLine($"Error: {response.ErrorMessage}");
HTTP Resilience
Provider DI helpers automatically call AddCisharpaiResilienceHandler() on their HttpClient registrations. This applies to OpenAI, Azure OpenAI, Azure AI Inference, Anthropic, Cohere, and embedding clients.
The standard handler uses Microsoft.Extensions.Http.Resilience / Polly with:
- Retries for transient failures: HTTP
408, 429, 5xx, HttpRequestException, and timeout failures.
Retry-After support, so 429 Too Many Requests can delay according to the server-provided header.
- 3 retry attempts, 500 ms initial delay, exponential backoff, and jitter.
- 60 second per-attempt timeout and 90 second total request timeout.
- Circuit breaker with 120 second sampling, 20% failure ratio, minimum 10 requests, and 15 second break duration.
After retries are exhausted, API-level HTTP errors become normal response failures (IsSuccess = false, ErrorMessage, and raw response body when available). Network/configuration problems may still throw.
When using Create(...), resilience is not added automatically. Register the named handler with resilience:
services.AddHttpClient("cisharpai")
.AddCisharpaiResilienceHandler();
For long-running streaming workloads, use AddCisharpaiStreamingResilienceHandler() on the streaming HttpClient registration. It removes the standard 60s/90s timeouts while keeping retry and circuit-breaker behavior.
Runtime Client Creation (no DI required)
When API keys or endpoints are not known at startup (multi-tenant apps, user-provided credentials), use the static Create factory method on each client. Register a single pooled handler once; create client instances on demand.
services.AddHttpClient("cisharpai");
var client = OpenAiChatCompletionClient.Create(
handlerFactory,
new OpenAiClientOptions { ApiKey = runtimeKey, DefaultModel = "gpt-4o" },
loggerFactory: loggerFactory);
All 9 clients support Create. Azure providers add an optional TokenCredential parameter for Azure AD auth. See references/runtime-configuration.md for all signatures and a multi-provider dispatch example.
Key notes:
- Client instances are cheap; TCP connections are pooled in the handler.
Create bypasses DI resilience handlers; add services.AddHttpClient("cisharpai").AddCisharpaiResilienceHandler() at startup if retries/timeouts/circuit breaking are needed.
- Azure OpenAI chat clients share learned routing fallbacks in-process per
(Endpoint, DeploymentName, ApiVersion), so later dynamically created clients reuse the working route after the first mismatch is discovered.
DI Client Factory (runtime creation with resilience)
When providers are chosen at runtime AND you want full DI benefits (resilience handlers, HttpClient pooling), use ICisharpaiClientFactory. Each provider has a strongly-typed configuration class.
services.AddCisharpaiClientFactory()
.AddOpenAiSupport()
.AddAnthropicSupport()
.AddAzureOpenAiSupport()
.AddAzureAiInferenceSupport()
.AddCohereSupport();
var factory = serviceProvider.GetRequiredService<ICisharpaiClientFactory>();
var config = new OpenAiClientConfiguration { ApiKey = "sk-...", DefaultModel = "gpt-4o" };
var result = factory.CreateChatCompletionClient(config);
if (result.IsSuccess) { }
else { }
Configuration classes: OpenAiClientConfiguration, AnthropicClientConfiguration, AzureOpenAiClientConfiguration (requires Endpoint + DeploymentName), AzureAiInferenceClientConfiguration (requires Endpoint + ModelId), CohereClientConfiguration.
Error handling: Returns CisharpaiClientFactoryResult<T> with IsSuccess/ErrorMessage โ no exceptions for unregistered providers or unsupported capabilities.
Testing: Use FakeClientFactoryProvider from Cisharpai.Testing with builder.AddFakeSupport().
Core Interfaces
IChatCompletionClient
public interface IChatCompletionClient : IHasFeatures
{
Task<ChatCompletionResponse> GetChatCompletionAsync(
ChatCompletionRequest request, CancellationToken ct = default);
}
IEmbeddingClient
public interface IEmbeddingClient : IHasFeatures
{
Task<EmbeddingResponse> GetEmbeddingsAsync(
EmbeddingRequest request, CancellationToken ct = default);
}
Feature Discovery
Optional capabilities are accessed via the Feature Collection Pattern:
var streaming = client.Features.Get<IStreamingChatFeature>();
if (streaming is not null)
{
await foreach (var chunk in streaming.GetChatCompletionStreamAsync(request))
Console.Write(chunk.Content);
}
Core DTOs (Immutable Records)
ChatCompletionRequest:
- Positional immutable record:
new ChatCompletionRequest(Messages: [...], Model: "gpt-4o")
- Properties:
Messages, Model?, Temperature?, MaxTokens?, IncludeRawResponse, ExtraParameters?
ChatCompletionResponse:
Content, Usage, IsSuccess, ErrorMessage, RawResponseJson, RawRequestJson, Refusal
LlmMessage:
Role, Content, ContentParts, ToolCallId, ToolCalls
- Use
LlmRole.User, LlmRole.Assistant, LlmRole.System, LlmRole.Tool; do not pass string roles.
- Factory:
LlmMessage.WithImage(text, filePath), LlmMessage.WithBase64Image(text, base64, mediaType)
EmbeddingRequest:
Input (string[]), Model?, InputType?, Dimensions?, EncodingFormat?, ExtraParameters?
EmbeddingResponse:
Embeddings (float[][]), Dimensions, Model, TotalTokens, IsSuccess, ErrorMessage
Feature Interfaces
All feature interfaces live in the Cisharpai.Features.Chat namespace (not Cisharpai.Features).
| Feature | Interface | Providers |
|---|
| JSON Output | IJsonOutputFeature | All 5 |
| Tool Calling | IToolCallingFeature | All 5 |
| Streaming | IStreamingChatFeature | All 5 |
| Grounded Chat | IGroundedChatFeature | Cohere only |
| Image Embedding | IImageEmbeddingFeature | Azure AI Inference, Cohere |
| Multimodal Embedding | IMultimodalEmbeddingFeature | Cohere only |
Provider-Specific Guides
Each provider has unique setup, model routing, and quirks.
See the reference files for detailed information:
Feature Guides
Error Handling Pattern
var response = await client.GetChatCompletionAsync(request);
if (!response.IsSuccess)
{
logger.LogError("LLM error: {Error}", response.ErrorMessage);
if (response.RawResponseJson is not null)
logger.LogDebug("Raw: {Raw}", response.RawResponseJson);
return;
}
var content = response.Content;
ExtraParameters Escape Hatch
Deep-merge arbitrary JSON into the provider request for bleeding-edge features:
var request = new ChatCompletionRequest(
Messages: [new LlmMessage(LlmRole.User, "Hello")],
ExtraParameters: JsonDocument.Parse("""
{
"top_p": 0.9,
"presence_penalty": 0.6
}
""").RootElement);
Consumption Pitfalls
- Import both
Cisharpai for interfaces and Cisharpai.Models for DTOs.
ChatCompletionRequest, LlmMessage, ToolDefinition, ToolCallingOptions, and JsonOutputOptions are immutable positional records. Prefer constructor/named-argument syntax, not object initializers.
LlmMessage takes LlmRole, not a string. Use new LlmMessage(LlmRole.User, "...").
- Tool calling uses
IToolCallingFeature.GetChatCompletionWithToolsAsync(...).
- JSON output uses
IJsonOutputFeature.GetChatCompletionWithJsonOutputAsync(...).
FakeChatCompletionClient queues responses with methods such as EnqueueResponse(...), not a public ResponseQueue property.
JsonOutputMode has two values: JsonMode (json_object, no schema) and JsonSchema (strict schema enforcement). There is no JsonObject value.
JsonOutputOptions positional record signature: (JsonOutputMode Mode, string? SchemaName = null, string? SchemaDescription = null, string? JsonSchema = null, bool Strict = true). SchemaName and JsonSchema are required when Mode == JsonSchema.
IJsonOutputFeature is in Cisharpai.Features.Chat, not Cisharpai.Features.
Project Structure
src/Cisharpai/ โ Core abstractions, interfaces, models, helpers
src/Cisharpai.OpenAi/ โ OpenAI provider
src/Cisharpai.Azure/ โ Azure OpenAI + Azure AI Inference
src/Cisharpai.Anthropic/ โ Anthropic provider
src/Cisharpai.Cohere/ โ Cohere provider
src/Cisharpai.Testing/ โ Fake clients for unit testing
src/Cisharpai.Tests/ โ Unit tests (all providers)
src/Cisharpai.Integration.Tests/ โ Integration tests (.NET 10 only)
Troubleshooting
Response returns IsSuccess = false:
Set IncludeRawResponse = true on the request and inspect RawResponseJson.
Feature returns null from Get<T>():
The provider doesn't support that feature. Check references/provider-features.md.
Build targets: Projects multitarget .NET 8.0 and .NET 10.