Compendium.Adapters.Mistral
1.0.0-preview.0
dotnet add package Compendium.Adapters.Mistral --version 1.0.0-preview.0
NuGet\Install-Package Compendium.Adapters.Mistral -Version 1.0.0-preview.0
<PackageReference Include="Compendium.Adapters.Mistral" Version="1.0.0-preview.0" />
<PackageVersion Include="Compendium.Adapters.Mistral" Version="1.0.0-preview.0" />
<PackageReference Include="Compendium.Adapters.Mistral" />
paket add Compendium.Adapters.Mistral --version 1.0.0-preview.0
#r "nuget: Compendium.Adapters.Mistral, 1.0.0-preview.0"
#:package Compendium.Adapters.Mistral@1.0.0-preview.0
#addin nuget:?package=Compendium.Adapters.Mistral&version=1.0.0-preview.0&prerelease
#tool nuget:?package=Compendium.Adapters.Mistral&version=1.0.0-preview.0&prerelease
Compendium.Adapters.Mistral
Direct adapter for the Mistral AI "la Plateforme" REST API (https://api.mistral.ai/v1) that implements IAIProvider from Compendium.Abstractions.AI.
| What | Value |
|---|---|
| Target framework | net9.0 |
| Abstractions pin | Compendium.Abstractions.AI 1.0.1 |
| HTTP stack | Typed HttpClient + Microsoft.Extensions.Http.Resilience (standard pipeline) + System.Text.Json |
| Auth | Authorization: Bearer <ApiKey> |
| Surface | Chat completions (sync + SSE streaming), embeddings (batched), tool calling, structured outputs, Pixtral vision |
| Tests | xUnit 2.9 + FluentAssertions + NSubstitute + RichardSzalay.MockHttp |
EU data residency — the headline value prop
Mistral AI hosts inference in France (EU). Payloads, including PII, never leave the EU. For European tenants subject to GDPR (or contractually required to keep data on EU soil), this adapter is the strongest default in the Compendium ecosystem. Pair it with Compendium.Adapters.OpenAI only when you need OpenAI-specific models.
| Need | Pick |
|---|---|
| GDPR / EU data residency | Compendium.Adapters.Mistral |
OpenAI-only models (e.g. o1) |
Compendium.Adapters.OpenAI |
| Gateway / model routing | Compendium.Adapters.OpenRouter |
| EU + Azure compliance overlay | Compendium.Adapters.AzureOpenAI (EU region) |
Installation
dotnet add package Compendium.Adapters.Mistral
Configuration
Register at startup:
services.AddCompendiumMistral(opt =>
{
opt.ApiKey = builder.Configuration["Mistral:ApiKey"]!;
opt.DefaultModel = "mistral-large-latest";
opt.DefaultEmbeddingModel = "mistral-embed";
});
…or bind from IConfiguration (section Mistral):
services.AddCompendiumMistral(builder.Configuration);
{
"Mistral": {
"ApiKey": "...",
"DefaultModel": "mistral-large-latest",
"DefaultEmbeddingModel": "mistral-embed",
"TimeoutSeconds": 120,
"MaxEmbeddingsBatchSize": 512
}
}
The standard resilience pipeline (AddStandardResilienceHandler) is applied automatically; tune retry / timeout behaviour by configuring MistralOptions.TimeoutSeconds and RetryAttempts.
Model catalog
| Model | Use case |
|---|---|
mistral-large-latest |
Top-tier reasoning, default chat model |
mistral-small-latest |
Cheap reasoning, integration tests |
mistral-medium-latest |
Balanced cost/quality |
open-mistral-7b, open-mixtral-8x7b |
Open-weight tiers |
codestral-latest |
Code-specialised (chat endpoint, same wire format) |
pixtral-12b-latest, pixtral-large-latest |
Vision (multimodal text + image) |
mistral-embed |
Embeddings (1024-dim) |
Usage
Chat (sync)
var provider = sp.GetRequiredService<IAIProvider>();
var result = await provider.CompleteAsync(new CompletionRequest
{
Model = "mistral-large-latest",
SystemPrompt = "Be concise.",
Messages = new List<Message> { Message.User("Pick a colour.") },
MaxTokens = 64
});
if (result.IsSuccess) Console.WriteLine(result.Value.Content);
Chat (streaming)
await foreach (var chunk in provider.StreamCompleteAsync(request))
{
if (chunk.IsSuccess) Console.Write(chunk.Value.ContentDelta);
}
The final chunk carries IsFinal=true, FinishReason, and Usage (token counts).
Codestral (code-specialised chat)
Codestral exposes the same /v1/chat/completions surface as the general chat models. Switch by setting DefaultModel:
services.AddCompendiumMistral(opt =>
{
opt.ApiKey = "...";
opt.DefaultModel = "codestral-latest";
});
ListModelsAsync marks Codestral with AIModel.Metadata["specialty"] = "code" so callers can route automatically.
Embeddings (batched)
EmbedAsync chunks the input list into requests no larger than MaxEmbeddingsBatchSize (default 512) and aggregates the embeddings, re-mapping indices to the original input order.
var result = await provider.EmbedAsync(new EmbeddingRequest
{
Model = "mistral-embed",
Inputs = manyTexts
});
Tool calling
using Compendium.Adapters.Mistral.Tools;
using Compendium.Abstractions.AI.Agents.Models;
var tools = new List<AgentTool>
{
new(
Name: "get_weather",
Description: "Returns the current weather for the named city.",
InputSchemaJson: """{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}""")
};
var request = new CompletionRequest
{
Model = "mistral-large-latest",
Messages = new List<Message> { Message.User("Weather in Paris?") }
}.WithTools(tools, toolChoice: "auto"); // "auto" | "any" | "none" | <tool-name>
var result = await provider.CompleteAsync(request);
foreach (var call in result.Value.GetToolCalls())
{
Console.WriteLine($"{call.ToolName}({call.ArgumentsJson})");
}
Tool definitions are serialised into Mistral's tools[] array; the assistant's tool_calls are surfaced as IReadOnlyList<AgentToolInvocation> via CompletionResponse.GetToolCalls().
Mistral specifics: Mistral uses
"any"(not"required") to force the model to emit a tool call. The adapter passes the value verbatim.
Structured outputs
using Compendium.Adapters.Mistral.StructuredOutputs;
var schema = """
{
"type":"object",
"properties":{ "city": {"type":"string"}, "tempC": {"type":"number"} },
"required":["city","tempC"],
"additionalProperties": false
}
""";
var request = new CompletionRequest
{
Model = "mistral-large-latest",
Messages = new List<Message> { Message.User("Weather in Paris as JSON.") }
}.WithStructuredOutput(schema, schemaName: "Weather");
// or: plain json_object mode (no schema)
request = request.WithJsonMode();
Globally opt every request into response_format: { type: "json_object" } with MistralOptions.UseStructuredOutputsByDefault = true.
Vision (Pixtral)
using Compendium.Adapters.Mistral.Vision;
var request = new CompletionRequest
{
Model = "pixtral-12b-latest",
Messages = new List<Message> { Message.User("Describe this image.") }
}.WithImages("https://example.com/cat.jpg"); // or a data:image/...;base64,... URL
var result = await provider.CompleteAsync(request);
WithImages(...) promotes the last user message into a Pixtral-style multimodal content-parts array ([{type:"text", text}, {type:"image_url", image_url:{url}}]). If no user message exists, a fresh one is appended.
Cancellation
Every async surface accepts a CancellationToken. The adapter:
- re-throws
OperationCanceledExceptionwhen the caller cancelled, so callers canawaitcleanly; - maps non-caller cancellations (timeouts) into
Result.Failure(AIErrors.Timeout(...)); - aborts streaming loops on cancellation between SSE chunks.
Error mapping
| HTTP | Error.Code |
|---|---|
| 401 | AI.InvalidApiKey |
| 402 | AI.InsufficientCredits |
| 404 | AI.ModelNotFound |
| 429 | AI.RateLimitExceeded |
| 5xx / other | AI.ProviderError |
| Timeout (non-cancellation) | AI.Timeout |
Mistral returns two error envelope shapes and the adapter parses both:
// OpenAI-compat shape:
{ "error": { "message": "...", "code": "...", "type": "..." } }
// Native Mistral shape (most common from the platform today):
{ "message": "..." | { "detail": "..." }, "type": "...", "code": "..." }
Observability
MistralOptions.EnableLogging = true emits the raw request/response bodies at Debug level via the injected ILogger<MistralHttpClient>. Keep it off in production — payloads may contain user PII (the very thing you picked Mistral for keeping inside the EU).
Production checklist
- EU region:
https://api.mistral.airesolves to a French (eu-west) endpoint by default — TLS handled by the runtime. Document this in your tenant DPA. - API key rotation: store
Mistral:ApiKeyin your Compendium secret store; rotate quarterly. Mistral supports listing+revoking keys via the platform UI. - Resilience:
AddStandardResilienceHandlercovers retries, jitter, and total-attempts budget. IncreaseTimeoutSecondsonly after confirming Mistral isn't rate-limiting you. - PII: leave
EnableLogging = falsein production unless debugging a specific tenant under a DPA carve-out. - Pricing:
mistral-large-latestis the priciest tier — gate it behind a tenant feature flag and fall back tomistral-small-latestfor non-premium workloads.
Sample
samples/01-eu-chat-with-pixtral wires up a Pixtral vision call and prints the EU-hosted reply. Run with MISTRAL_API_KEY=... dotnet run --project samples/01-eu-chat-with-pixtral.
Integration tests
Live tests live in tests/Integration/Compendium.Adapters.Mistral.IntegrationTests. They:
- skip automatically when
MISTRAL_API_KEYis unset; - are excluded from the default CI run via
--filter "FullyQualifiedName!~IntegrationTests"; - hit
mistral-small-latest/mistral-embedso they cost cents per full run.
export MISTRAL_API_KEY=...
dotnet test -c Release --filter "FullyQualifiedName~IntegrationTests"
SDK choice (ADR)
Decision: hand-rolled HttpClient + System.Text.Json, no Mistral official SDK dependency.
Reasons:
- Mirrors the proven
Compendium.Adapters.OpenAIandCompendium.Adapters.Deepseekpatterns. - Lets the framework's pinned
Microsoft.Extensions.Http.Resiliencepipeline handle retries / circuit breaking. - Mistral's wire format is small, stable, and OpenAI-compat at
/v1/chat/completionsand/v1/embeddings— the only real divergence is the dual error envelope, which we handle inline.
Out of scope (first preview)
- FIM (fill-in-the-middle)
/v1/fim/completions— Codestral-specific, separate API shape; deferred to v1.1. - Agents API (
/v1/agents) — Mistral's bespoke agent orchestration; deferred to v1.1. - Custom embeddings
dimensionsparameter — not exposed byCompendium.Abstractions.AI1.0.1'sEmbeddingRequest. Will follow when the abstraction adds it.
License
MIT — same as Compendium itself.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 was computed. 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. |
-
net9.0
- Compendium.Abstractions.AI (>= 1.0.1)
- Compendium.Core (>= 1.0.1)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.16)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.16)
- Microsoft.Extensions.Http (>= 9.0.16)
- Microsoft.Extensions.Http.Resilience (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.16)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.16)
- System.Text.Json (>= 9.0.16)
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.0.0-preview.0 | 51 | 5/21/2026 |