Skip to content

Commit ec46fd3

Browse files
Add ASP.NET Core example and update README (#499)
* chore: add ASP.NET Core example and update README * Update examples/ASP.NET Core/README.md Co-authored-by: Jesse Squire <[email protected]> * Update examples/ASP.NET Core/README.md Co-authored-by: Jesse Squire <[email protected]> * fix: missing table content for new paragraph * fix: rename structure new example project * chore: refactor example README.md to follow Windows first approach * docs: add Additional resources paragraph on README.md * chore: remove api secret and unused records * remove record from new example * Update examples/aspnet-core/appsettings.json Co-authored-by: Jesse Squire <[email protected]> * Update examples/aspnet-core/aspnet-core.csproj Co-authored-by: Jesse Squire <[email protected]> * chore: fix CI by excluding aspnet-core subdirectory * chore: update README to use ChatClient instead of OpenAIClient --------- Co-authored-by: Jesse Squire <[email protected]>
1 parent 33e8f6f commit ec46fd3

File tree

7 files changed

+282
-0
lines changed

7 files changed

+282
-0
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena
1515
- [Namespace organization](#namespace-organization)
1616
- [Using the async API](#using-the-async-api)
1717
- [Using the `OpenAIClient` class](#using-the-openaiclient-class)
18+
- [How to use dependency injection](#how-to-use-dependency-injection)
1819
- [How to use chat completions with streaming](#how-to-use-chat-completions-with-streaming)
1920
- [How to use chat completions with tools and function calling](#how-to-use-chat-completions-with-tools-and-function-calling)
2021
- [How to use chat completions with structured outputs](#how-to-use-chat-completions-with-structured-outputs)
@@ -140,6 +141,45 @@ AudioClient ttsClient = client.GetAudioClient("tts-1");
140141
AudioClient whisperClient = client.GetAudioClient("whisper-1");
141142
```
142143

144+
## How to use dependency injection
145+
146+
The OpenAI clients are **thread-safe** and can be safely registered as **singletons** in ASP.NET Core's Dependency Injection container. This maximizes resource efficiency and HTTP connection reuse.
147+
148+
Register the `ChatClient` as a singleton in your `Program.cs`:
149+
150+
```csharp
151+
builder.Services.AddSingleton<ChatClient>(serviceProvider =>
152+
{
153+
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
154+
155+
return new ChatClient(apiKey);
156+
});
157+
```
158+
159+
Then inject and use the client in your controllers or services:
160+
161+
```csharp
162+
[ApiController]
163+
[Route("api/[controller]")]
164+
public class ChatController : ControllerBase
165+
{
166+
private readonly ChatClient _chatClient;
167+
168+
public ChatController(ChatClient chatClient)
169+
{
170+
_chatClient = chatClient;
171+
}
172+
173+
[HttpPost("complete")]
174+
public async Task<IActionResult> CompleteChat([FromBody] string message)
175+
{
176+
ChatCompletion completion = await _chatClient.CompleteChatAsync(message);
177+
178+
return Ok(new { response = completion.Content[0].Text });
179+
}
180+
}
181+
```
182+
143183
## How to use chat completions with streaming
144184

145185
When you request a chat completion, the default behavior is for the server to generate it in its entirety before sending it back in a single response. Consequently, long chat completions can require waiting for several seconds before hearing back from the server. To mitigate this, the OpenAI REST API supports the ability to stream partial results back as they are being generated, allowing you to start processing the beginning of the completion before it is finished.

examples/OpenAI.Examples.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77

88
<LangVersion>latest</LangVersion>
99
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<!-- Exclude the aspnet-core subdirectory from compilation -->
13+
<Compile Remove="aspnet-core/**" />
14+
<Content Remove="aspnet-core/**" />
15+
<None Remove="aspnet-core/**" />
16+
</ItemGroup>
1017
<ItemGroup>
1118
<ProjectReference Include="..\src\OpenAI.csproj" />
1219
</ItemGroup>

examples/aspnet-core/Program.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.ClientModel;
2+
using OpenAI.Chat;
3+
4+
var builder = WebApplication.CreateBuilder(args);
5+
6+
// Add services to the container.
7+
builder.Services.AddEndpointsApiExplorer();
8+
builder.Services.AddSwaggerGen();
9+
10+
builder.Services.AddSingleton<ChatClient>(serviceProvider => new ChatClient(builder.Configuration["OpenAI:Model"],
11+
new ApiKeyCredential(builder.Configuration["OpenAI:ApiKey"]
12+
?? Environment.GetEnvironmentVariable("OPENAI_API_KEY")
13+
?? throw new InvalidOperationException("OpenAI API key not found")))
14+
);
15+
builder.Services.AddScoped<ChatHttpHandler>();
16+
17+
18+
var app = builder.Build();
19+
20+
// Configure the HTTP request pipeline.
21+
if (app.Environment.IsDevelopment())
22+
{
23+
app.UseSwagger();
24+
app.UseSwaggerUI();
25+
}
26+
27+
app.UseHttpsRedirection();
28+
29+
var chatHandler = app.Services.GetRequiredService<ChatHttpHandler>();
30+
31+
app.MapPost("/chat/complete", chatHandler.HandleChatRequest);
32+
33+
app.Run();
34+
35+
public class ChatHttpHandler
36+
{
37+
private readonly ChatClient _client;
38+
private readonly ILogger<ChatHttpHandler> _logger;
39+
40+
// Chat completion endpoint using injected ChatClient client
41+
public ChatHttpHandler(ChatClient client, ILogger<ChatHttpHandler> logger)
42+
{
43+
_client = client;
44+
_logger = logger;
45+
}
46+
47+
public async Task<ChatResponse> HandleChatRequest(ChatRequest request)
48+
{
49+
_logger.LogInformation("Handling chat request: {Message}", request.Message);
50+
var completion = await _client.CompleteChatAsync(request.Message);
51+
return new ChatResponse(completion.Value.Content[0].Text);
52+
}
53+
}
54+
55+
public class ChatRequest
56+
{
57+
public string Message { get; set; }
58+
59+
public ChatRequest(string message)
60+
{
61+
Message = message;
62+
}
63+
}
64+
65+
public class ChatResponse
66+
{
67+
public string Response { get; set; }
68+
69+
public ChatResponse(string response)
70+
{
71+
Response = response;
72+
}
73+
}

examples/aspnet-core/README.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# OpenAI ASP.NET Core Example
2+
3+
This example demonstrates how to use the OpenAI .NET client library with ASP.NET Core's dependency injection container, registering a `ChatClient` as a singleton for optimal performance and resource usage.
4+
5+
## Features
6+
7+
- **Singleton Registration**: ChatClient registered as singleton in DI container
8+
- **Thread-Safe**: Demonstrates concurrent usage for chat completion endpoints
9+
- **Configurable Model**: Model selection via configuration (appsettings.json)
10+
- **Modern ASP.NET Core**: Uses minimal APIs with async/await patterns
11+
12+
## Prerequisites
13+
14+
- .NET 8.0 or later
15+
- OpenAI API key
16+
17+
## Setup
18+
19+
1. **Set your OpenAI API key** using one of these methods:
20+
21+
**Environment Variable (Recommended):**
22+
23+
```powershell
24+
$env:OPENAI_API_KEY = "your-api-key-here"
25+
```
26+
27+
**Configuration (appsettings.json):**
28+
29+
```json
30+
{
31+
"OpenAI": {
32+
"Model": "gpt-4o-mini",
33+
"ApiKey": "your-api-key-here"
34+
}
35+
}
36+
```
37+
38+
2. **Install dependencies:**
39+
40+
```powershell
41+
dotnet restore
42+
```
43+
44+
3. **Run the application:**
45+
46+
```powershell
47+
dotnet run
48+
```
49+
50+
## API Endpoints
51+
52+
### Chat Completion
53+
54+
- **POST** `/chat/complete`
55+
- **Request Body:**
56+
57+
```json
58+
{
59+
"message": "Hello, how are you?"
60+
}
61+
```
62+
63+
- **Response:**
64+
65+
```json
66+
{
67+
"response": "I'm doing well, thank you for asking! How can I help you today?"
68+
}
69+
```
70+
71+
## Testing with PowerShell
72+
73+
**Chat Completion:**
74+
75+
```powershell
76+
Invoke-RestMethod -Uri "https://localhost:5000/chat/complete" `
77+
-Method POST `
78+
-ContentType "application/json" `
79+
-Body '{"message": "What is the capital of France?"}'
80+
```
81+
82+
## Key Implementation Details
83+
84+
### Singleton Registration
85+
86+
```csharp
87+
builder.Services.AddSingleton<ChatClient>(serviceProvider => new ChatClient(
88+
builder.Configuration["OpenAI:Model"],
89+
new ApiKeyCredential(builder.Configuration["OpenAI:ApiKey"]
90+
?? Environment.GetEnvironmentVariable("OPENAI_API_KEY")
91+
?? throw new InvalidOperationException("OpenAI API key not found")))
92+
);
93+
```
94+
95+
### Dependency Injection Usage
96+
97+
```csharp
98+
app.MapPost("/chat/complete", async (ChatRequest request, ChatClient client) =>
99+
{
100+
var completion = await client.CompleteChatAsync(request.Message);
101+
102+
return new ChatResponse(completion.Value.Content[0].Text);
103+
});
104+
```
105+
106+
## Why Singleton?
107+
108+
- **Thread-Safe**: `ChatClient` is thread-safe and can handle concurrent requests
109+
- **Resource Efficient**: Reuses HTTP connections and avoids creating multiple instances
110+
- **Performance**: Reduces object allocation overhead
111+
- **Stateless**: Clients don't maintain per-request state
112+
113+
## Swagger UI
114+
115+
When running in development mode, you can access the Swagger UI at:
116+
117+
- `https://localhost:7071/swagger`
118+
119+
This provides an interactive interface to test the API endpoints.
120+
121+
## Additional Resources
122+
123+
- [Tutorial: Create a minimal API with ASP.NET Core](https://learn.microsoft.com/aspnet/core/tutorials/min-web-api)
124+
- [.NET dependency injection](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection)
125+
- [Logging in C# and .NET](https://learn.microsoft.com/dotnet/core/extensions/logging)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}

examples/aspnet-core/appsettings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*",
9+
"OpenAI":
10+
{
11+
"Model": "gpt-4.1-mini",
12+
"ApiKey": ""
13+
}
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<RootNamespace>ASP.NET_Core</RootNamespace>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="OpenAI" Version="2.2.0" />
12+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
13+
</ItemGroup>
14+
15+
</Project>

0 commit comments

Comments
 (0)