Skip to content

Commit e5fdd90

Browse files
Merge pull request #50081 from dotnet/main
Merge main into live
2 parents 181fc48 + 7753ddf commit e5fdd90

26 files changed

+329
-56
lines changed

.github/copilot-instructions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ For snippets >6 lines:
4444
1. All code should use the latest stable versions/features.
4545
1. Create examples in both C# and Visual Basic unless the article referencing the snippet resides in the in the `csharp`, `fsharp`, and `visual-basic` language folders.
4646
1. When you add code, use code comments sparingly because they don't get localized. You can use them to briefly clarify code-specific details (such as logic, parameters, or edge cases). Put any critical information and context in the markdown text of the referencing article.
47+
1. IMPORTANT: For created code, always try to encapsulate it in an standalone executable (e.g. `dotnet fsi myFile.fsx` or `dotnet run myFile.cs`, add the necessary boilerplate/imports/usings where needed, and execute it.). Run it, and for every code snippet, include a PR commentary checking each code sample and proving what it has produced - this can be diagnostics, standard output, or a result value. That standalone file is just for the purpose of verification within copilot's execution environment, the published docs snippet should remain a subset as you would normally write to maximize clarity.
4748

4849
## File Naming
4950

@@ -69,4 +70,4 @@ When assigned an issue or directly given a task in GitHub:
6970
3. Check for build warnings in the OpenPublishing.Build status check
7071
4. If warnings exist:
7172
- Click "View Details" to open the build report
72-
- Resolve any build warnings you introduced
73+
- Resolve any build warnings you introduced

.github/workflows/dependabot-bot.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ jobs:
4040
- name: "Print manual run reason"
4141
if: ${{ github.event_name == 'workflow_dispatch' }}
4242
run: |
43-
echo "Reason: ${{ github.event.inputs.reason }}"
43+
echo "Reason: $REASON"
44+
env:
45+
REASON: ${{ github.event.inputs.reason }}
4446
# Run the .NET dependabot-bot tool
4547
- name: dependabot-bot
4648
id: dependabot-bot

.github/workflows/quest-bulk.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ jobs:
3535
- name: "Print manual bulk import run reason"
3636
if: ${{ github.event_name == 'workflow_dispatch' }}
3737
run: |
38-
echo "Reason: ${{ github.event.inputs.reason }}"
38+
echo "Reason: $REASON"
39+
env:
40+
REASON: ${{ github.event.inputs.reason }}
3941

4042
- name: Azure OpenID Connect
4143
id: azure-oidc-auth

.github/workflows/quest.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ jobs:
3636
- name: "Print manual run reason"
3737
if: ${{ github.event_name == 'workflow_dispatch' }}
3838
run: |
39-
echo "Reason: ${{ github.event.inputs.reason }}"
40-
echo "Issue number: ${{ github.event.inputs.issue }}"
39+
echo "Reason: $REASON"
40+
echo "Issue number: $ISSUENUMBER"
41+
env:
42+
REASON: ${{ github.event.inputs.reason }}
43+
ISSUENUMBER: ${{ github.event.inputs.issue }}
4144

4245
- name: Azure OpenID Connect
4346
id: azure-oidc-auth
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// <SnippetSetup>
2+
using Aspire.Azure.AI.OpenAI;
3+
using Microsoft.Extensions.AI;
4+
using OpenAI;
5+
6+
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
7+
8+
// Add the Azure OpenAI client using hosting integration.
9+
AspireAzureOpenAIClientBuilder openai = builder.AddAzureOpenAIClient("openai");
10+
// </SnippetSetup>
11+
12+
// <SnippetAddImageGenerator>
13+
// Register the image generator with dependency injection.
14+
ImageGeneratorBuilder imageBuilder = builder.Services.AddImageGenerator(services =>
15+
{
16+
OpenAIClient openAiClient = services.GetRequiredService<OpenAIClient>();
17+
OpenAI.Images.ImageClient imageClient = openAiClient.GetImageClient("gpt-image-1");
18+
#pragma warning disable MEAI001 // Type is for evaluation purposes only.
19+
return imageClient.AsIImageGenerator();
20+
#pragma warning restore MEAI001
21+
});
22+
// </SnippetAddImageGenerator>
23+
24+
// <SnippetConfigureOptions>
25+
imageBuilder.ConfigureOptions(options =>
26+
{
27+
options.MediaType = "image/png";
28+
}).UseLogging();
29+
// </SnippetConfigureOptions>
30+
31+
WebApplication app = builder.Build();
32+
33+
// <SnippetUseImageGenerator>
34+
// Use the image generator in an endpoint.
35+
app.MapPost("/generate-image", async (IImageGenerator generator, string prompt) =>
36+
{
37+
ImageGenerationResponse response = await generator.GenerateImagesAsync(prompt);
38+
DataContent dataContent = response.Contents.OfType<DataContent>().First();
39+
40+
return Results.File(dataContent.Data.ToArray(), "image/png");
41+
});
42+
// </SnippetUseImageGenerator>
43+
44+
app.Run();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "http://localhost:5219",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
13+
"https": {
14+
"commandName": "Project",
15+
"dotnetRunMessages": true,
16+
"launchBrowser": true,
17+
"applicationUrl": "https://localhost:7210;http://localhost:5219",
18+
"environmentVariables": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
}
21+
}
22+
}
23+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<NoWarn>$(NoWarn);MEAI001</NoWarn>
8+
<UserSecretsId>a9e545e9-e2b5-4e1b-81ce-217ca2d281c6</UserSecretsId>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Aspire.Azure.AI.OpenAI" Version="13.0.0-preview.1.25560.3" />
13+
<PackageReference Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
14+
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.0-preview.1.25560.10" />
15+
</ItemGroup>
16+
17+
</Project>
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+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*",
9+
"ConnectionStrings": {
10+
"openai": "Endpoint=https://your-endpoint.com/;Key=your-api-key"
11+
}
12+
}

docs/ai/quickstarts/text-to-image.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,65 @@ You can customize image generation by providing other options such as size, resp
100100
- <xref:Microsoft.Extensions.AI.ImageGenerationOptions.RawRepresentationFactory>: The callback that creates the raw representation of the image generation options from an underlying implementation.
101101
- <xref:Microsoft.Extensions.AI.ImageGenerationOptions.ResponseFormat>: Options are <xref:Microsoft.Extensions.AI.ImageGenerationResponseFormat.Uri>, <xref:Microsoft.Extensions.AI.ImageGenerationResponseFormat.Data>, and <xref:Microsoft.Extensions.AI.ImageGenerationResponseFormat.Hosted>.
102102
103+
## Use hosting integration
104+
105+
When you build web apps or hosted services, you can integrate image generation using dependency injection and hosting patterns. This approach provides better lifecycle management, configuration integration, and testability.
106+
107+
### Configure hosting services
108+
109+
The `Aspire.Azure.AI.OpenAI` package provides extension methods to register Azure OpenAI services with your application's dependency injection container:
110+
111+
1. Add the necessary packages to your web application:
112+
113+
```dotnetcli
114+
dotnet add package Aspire.Azure.AI.OpenAI --prerelease
115+
dotnet add package Azure.AI.OpenAI
116+
dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease
117+
```
118+
119+
1. Configure the Azure OpenAI client and image generator in your `Program.cs` file:
120+
121+
:::code language="csharp" source="snippets/text-to-image/hosting/Program.cs" id="SnippetSetup":::
122+
123+
The <xref:Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions.AddAzureOpenAIClient(Microsoft.Extensions.Hosting.IHostApplicationBuilder,System.String,System.Action{Aspire.Azure.AI.OpenAI.AzureOpenAISettings},System.Action{Azure.Core.Extensions.IAzureClientBuilder{Azure.AI.OpenAI.AzureOpenAIClient,Azure.AI.OpenAI.AzureOpenAIClientOptions}})> method registers the Azure OpenAI client with dependency injection. The connection string (named `"openai"`) is retrieved from configuration, typically from `appsettings.json` or environment variables:
124+
125+
```json
126+
{
127+
"ConnectionStrings": {
128+
"openai": "Endpoint=https://your-resource-name.openai.azure.com/;Key=your-api-key"
129+
}
130+
}
131+
```
132+
133+
1. Register the <xref:Microsoft.Extensions.AI.IImageGenerator> service with dependency injection:
134+
135+
:::code language="csharp" source="snippets/text-to-image/hosting/Program.cs" id="SnippetAddImageGenerator":::
136+
137+
The <xref:Microsoft.Extensions.DependencyInjection.ImageGeneratorBuilderServiceCollectionExtensions.AddImageGenerator*> method registers the image generator as a singleton service that can be injected into controllers, services, or minimal API endpoints.
138+
139+
1. Add options and logging::
140+
141+
:::code language="csharp" source="snippets/text-to-image/hosting/Program.cs" id="SnippetConfigureOptions":::
142+
143+
The preceding code:
144+
145+
- Configures options by calling the <xref:Microsoft.Extensions.AI.ConfigureOptionsImageGeneratorBuilderExtensions.ConfigureOptions(Microsoft.Extensions.AI.ImageGeneratorBuilder,System.Action{Microsoft.Extensions.AI.ImageGenerationOptions})> extension method on the <xref:Microsoft.Extensions.AI.ImageGeneratorBuilder>. This method configures the <xref:Microsoft.Extensions.AI.ImageGenerationOptions> to be passed to the next generator in the pipeline.
146+
- Adds logging to the image generator pipeline by calling the <xref:Microsoft.Extensions.AI.LoggingImageGeneratorBuilderExtensions.UseLogging(Microsoft.Extensions.AI.ImageGeneratorBuilder,Microsoft.Extensions.Logging.ILoggerFactory,System.Action{Microsoft.Extensions.AI.LoggingImageGenerator})> extension method.
147+
148+
### Use the image generator in endpoints
149+
150+
Once registered, you can inject `IImageGenerator` into your endpoints or services:
151+
152+
:::code language="csharp" source="snippets/text-to-image/hosting/Program.cs" id="SnippetUseImageGenerator":::
153+
154+
This hosting approach provides several benefits:
155+
156+
- **Configuration management**: Connection strings and settings are managed through the .NET configuration system.
157+
- **Dependency injection**: The image generator is available throughout your application via DI.
158+
- **Lifecycle management**: Services are properly initialized and disposed of by the hosting infrastructure.
159+
- **Testability**: Mock implementations can be easily substituted for testing.
160+
- **Integration with .NET Aspire**: When using .NET Aspire, the `AddAzureOpenAIClient` method integrates with service discovery and telemetry.
161+
103162
## Best practices
104163

105164
When implementing text-to-image generation in your applications, consider these best practices:

0 commit comments

Comments
 (0)