Compendium.Adapters.OpenAI
1.0.0-preview.0
dotnet add package Compendium.Adapters.OpenAI --version 1.0.0-preview.0
NuGet\Install-Package Compendium.Adapters.OpenAI -Version 1.0.0-preview.0
<PackageReference Include="Compendium.Adapters.OpenAI" Version="1.0.0-preview.0" />
<PackageVersion Include="Compendium.Adapters.OpenAI" Version="1.0.0-preview.0" />
<PackageReference Include="Compendium.Adapters.OpenAI" />
paket add Compendium.Adapters.OpenAI --version 1.0.0-preview.0
#r "nuget: Compendium.Adapters.OpenAI, 1.0.0-preview.0"
#:package Compendium.Adapters.OpenAI@1.0.0-preview.0
#addin nuget:?package=Compendium.Adapters.OpenAI&version=1.0.0-preview.0&prerelease
#tool nuget:?package=Compendium.Adapters.OpenAI&version=1.0.0-preview.0&prerelease
Compendium.Adapters.OpenAI
Direct adapter for the OpenAI REST API (https://api.openai.com/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>, optional OpenAI-Organization / OpenAI-Project headers |
| Surface | Chat completions (sync + SSE streaming), embeddings (batched), tool calling, structured outputs |
| Tests | xUnit 2.9 + FluentAssertions + NSubstitute + RichardSzalay.MockHttp |
Sits alongside Compendium.Adapters.OpenRouter (gateway routing). Use this adapter when you want to talk directly to OpenAI — for latency, billing isolation, or OpenAI-only models (e.g. text-embedding-3-large).
Installation
dotnet add package Compendium.Adapters.OpenAI
Configuration
Register at startup:
services.AddCompendiumOpenAI(opt =>
{
opt.ApiKey = builder.Configuration["OpenAI:ApiKey"]!;
opt.DefaultModel = "gpt-4o-mini";
opt.DefaultEmbeddingModel = "text-embedding-3-small";
});
…or bind from IConfiguration (section OpenAI):
services.AddCompendiumOpenAI(builder.Configuration);
{
"OpenAI": {
"ApiKey": "sk-...",
"DefaultModel": "gpt-4o-mini",
"DefaultEmbeddingModel": "text-embedding-3-small",
"Organization": "org-...", // optional
"Project": "proj_...", // optional
"TimeoutSeconds": 120,
"MaxEmbeddingsBatchSize": 2048
}
}
The standard resilience pipeline (AddStandardResilienceHandler) is applied automatically; tune retry/timeout behaviour by configuring OpenAIOptions.TimeoutSeconds and RetryAttempts.
Usage
Chat (sync)
var provider = sp.GetRequiredService<IAIProvider>();
var result = await provider.CompleteAsync(new CompletionRequest
{
Model = "gpt-4o-mini",
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).
Embeddings (batched)
EmbedAsync chunks the input list into requests no larger than MaxEmbeddingsBatchSize (default 2048, OpenAI's hard limit) and aggregates the embeddings, re-mapping indices to the original input order.
var result = await provider.EmbedAsync(new EmbeddingRequest
{
Model = "text-embedding-3-small",
Inputs = manyTexts
});
Tool calling
using Compendium.Adapters.OpenAI.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 = "gpt-4o-mini",
Messages = new List<Message> { Message.User("What's the weather in Paris?") }
}.WithTools(tools, toolChoice: "auto");
var result = await provider.CompleteAsync(request);
foreach (var call in result.Value.GetToolCalls())
{
Console.WriteLine($"{call.ToolName}({call.ArgumentsJson})");
}
Tool definitions are serialised into OpenAI's tools[] array; the assistant's tool_calls are surfaced as IReadOnlyList<AgentToolInvocation> via CompletionResponse.GetToolCalls(). ResultText / Latency on each invocation are left empty/zero — your tool registry fills them in when it executes the call.
Structured outputs
using Compendium.Adapters.OpenAI.StructuredOutputs;
var schema = """
{
"type":"object",
"properties":{ "city": {"type":"string"}, "tempC": {"type":"number"} },
"required":["city","tempC"],
"additionalProperties": false
}
""";
var request = new CompletionRequest
{
Model = "gpt-4o-mini",
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 OpenAIOptions.UseStructuredOutputsByDefault = true.
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 |
Observability
OpenAIOptions.EnableLogging = true emits the raw request/response bodies at Debug level via the injected ILogger<OpenAIHttpClient>. Keep it off in production — payloads can contain user PII.
Sample
samples/01-chat-with-tool wires up a 1-tool agent and dumps the model's tool-call decision. Run with OPENAI_API_KEY=sk-... dotnet run --project samples/01-chat-with-tool.
Integration tests
Live tests live in tests/Integration/Compendium.Adapters.OpenAI.IntegrationTests. They:
- skip automatically when
OPENAI_API_KEYis unset; - are excluded from the default CI run via
--filter "FullyQualifiedName!~IntegrationTests"; - hit
gpt-4o-mini/text-embedding-3-smallso they cost cents per full run.
export OPENAI_API_KEY=sk-...
dotnet test -c Release --filter "FullyQualifiedName~IntegrationTests"
SDK choice (ADR)
Decision: hand-rolled HttpClient + System.Text.Json, no OpenAI official SDK dependency.
Reasons:
- Avoids the heavy transitive graph and rapid version churn of the official SDK.
- Lets the framework's pinned
Microsoft.Extensions.Http.Resiliencepipeline handle retries / circuit breaking. - Mirrors the proven
Compendium.Adapters.OpenRouterpattern (98.7 % coverage). - The wire format is small and stable — chat / embeddings / tools /
response_format.
If the official SDK stabilises (sane transitive graph, stable type surface), we may pivot — document any switch with an updated ADR in docs/.
Out of scope (first preview)
- Realtime API, Assistants API, fine-tuning, batch API.
- Image generation / vision — separate
Compendium.Adapters.OpenAI.Imagespackage if/when needed. - Azure OpenAI — separate adapter (different auth and URL shape).
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.1.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 | 32 | 5/17/2026 |