Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AddAzureOpenAIChatClient #6225

Merged
merged 24 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a708719
Add AddAzureOpenAIChatClient
SteveSandersonMS Oct 10, 2024
ef23ed8
Set exact dependency versions in MEAI
SteveSandersonMS Oct 25, 2024
936de6c
Fix typo
SteveSandersonMS Oct 25, 2024
3962236
Handle connection string missing values
SteveSandersonMS Oct 25, 2024
1673d92
Handle connection string including duplicate values
SteveSandersonMS Oct 25, 2024
d5eb3d6
Change to chaining API
SteveSandersonMS Oct 25, 2024
a8ad9a7
Update dependencies
SteveSandersonMS Nov 26, 2024
4854d02
Update Aspire.Azure.AI.OpenAI APIs
SteveSandersonMS Nov 26, 2024
7b6be14
Unit tests
SteveSandersonMS Nov 26, 2024
27e40e5
Same for embedding generator
SteveSandersonMS Nov 26, 2024
d280001
Open telemetry by default
SteveSandersonMS Nov 26, 2024
5067628
Tests for configuring middleware
SteveSandersonMS Nov 26, 2024
20bcdcd
Fix namespace
SteveSandersonMS Nov 26, 2024
0864053
Add AspireOpenAIClientBuilder
SteveSandersonMS Nov 26, 2024
a3c4694
Add OpenAI variants
SteveSandersonMS Nov 26, 2024
71cf2d6
OpenAI unit tests
SteveSandersonMS Nov 26, 2024
c2d2580
Avoid duplicated logic
SteveSandersonMS Nov 26, 2024
4e07616
Remove incorrect remarks
SteveSandersonMS Nov 26, 2024
61b1ddb
Factor out some shared logic
SteveSandersonMS Nov 27, 2024
b3e997a
Add design comment
SteveSandersonMS Nov 27, 2024
0237c9b
In E2E test, pass deployment name manually for consistency with exist…
SteveSandersonMS Nov 27, 2024
4c29078
Expose "DisableTracing" flag on AspireOpenAIClientBuilder
SteveSandersonMS Nov 27, 2024
225cfec
Test tidyup
SteveSandersonMS Nov 27, 2024
6faa8e2
Control tracing/telemetry using higher-level OpenAI/AzureOpenAI config
SteveSandersonMS Nov 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<!-- Azure SDK for .NET dependencies -->
<PackageVersion Include="Azure.AI.OpenAI" Version="2.0.0" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.1.0-beta.2" />
<PackageVersion Include="Azure.Data.Tables" Version="12.9.1" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" />
<PackageVersion Include="Azure.Messaging.EventHubs" Version="5.11.5" />
Expand Down Expand Up @@ -80,6 +80,8 @@
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="$(MicrosoftEntityFrameworkCoreToolsPackageVersion)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="$(MicrosoftEntityFrameworkCoreDesignPackageVersion)" />
<!-- runtime dependencies-->
<PackageVersion Include="Microsoft.Extensions.AI" Version="$(MicrosoftExtensionsAIPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="$(MicrosoftExtensionsAIPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
Expand Down Expand Up @@ -116,7 +118,7 @@
<PackageVersion Include="NATS.Net" Version="2.5.3" />
<PackageVersion Include="Npgsql.DependencyInjection" Version="8.0.6" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlEntityFrameworkCorePostgreSQLPackageVersion)" />
<PackageVersion Include="OpenAI" Version="2.0.0" />
<PackageVersion Include="OpenAI" Version="2.1.0-beta.2" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="8.23.60" />
<PackageVersion Include="Oracle.ManagedDataAccess.OpenTelemetry" Version="23.6.0" />
<PackageVersion Include="Polly.Core" Version="8.4.2" />
Expand Down
1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<MicrosoftDotNetXUnitExtensionsPackageVersion>9.0.0-beta.24516.2</MicrosoftDotNetXUnitExtensionsPackageVersion>
<MicrosoftDotNetBuildTasksInstallersPackageVersion>9.0.0-beta.24516.2</MicrosoftDotNetBuildTasksInstallersPackageVersion>
<MicrosoftDotNetBuildTasksWorkloadsPackageVersion>9.0.0-beta.24516.2</MicrosoftDotNetBuildTasksWorkloadsPackageVersion>
<MicrosoftExtensionsAIPackageVersion>9.0.1-preview.1.24570.5</MicrosoftExtensionsAIPackageVersion>
<MicrosoftExtensionsHttpResiliencePackageVersion>9.0.0</MicrosoftExtensionsHttpResiliencePackageVersion>
<MicrosoftExtensionsDiagnosticsTestingPackageVersion>9.0.0</MicrosoftExtensionsDiagnosticsTestingPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>8.0.0</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
<base href="/" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="OpenAIEndToEnd.WebStory.styles.css" />
<HeadOutlet @rendermode="InteractiveServer" />
<HeadOutlet @rendermode="@renderMode" />
</head>

<body>
<Routes @rendermode="InteractiveServer" />
<Routes @rendermode="@renderMode" />
<script src="_framework/blazor.web.js"></script>
</body>

</html>

@code {
IComponentRenderMode renderMode = new InteractiveServerRenderMode(prerender: false);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@page "/"
@rendermode @(new InteractiveServerRenderMode(prerender: false))
@using OpenAI
@using OpenAI.Chat
@inject OpenAIClient aiClient
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@page "/useichatclient"
@using Microsoft.Extensions.AI
@inject IChatClient aiClient
@inject ILogger<Home> logger
@inject IConfiguration configuration

<div class="storybox" style="margin: 25%">
@foreach (var message in chatMessages.Where(m => m.Role == ChatRole.Assistant))
{
<p style="font-size: 3em;">@message.Text</p>
}

<button @onclick="GenerateNextParagraph" autofocus>Generate</button>
</div>

@code {
private List<ChatMessage> chatMessages = new List<ChatMessage>
{
new(ChatRole.System, "Pick a random topic and write a sentence of a fictional story about it.")
};

private async Task GenerateNextParagraph()
{
if (chatMessages.Count > 1)
{
chatMessages.Add(new (ChatRole.User, "Write the next sentence in the story."));
}

var response = await aiClient.CompleteAsync(chatMessages);
chatMessages.Add(response.Message);
}

protected override async Task OnInitializedAsync()
{
await GenerateNextParagraph();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
</ItemGroup>

<Import Project="$(RepoRoot)\src\Components\Aspire.OpenAI\MEAIPackageOverrides.targets" />

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

builder.AddServiceDefaults();

builder.AddAzureOpenAIClient("openai");
builder.AddAzureOpenAIClient("openai").AddChatClient();

// Add services to the container.
builder.Services.AddRazorComponents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@
<ProjectReference Include="..\Aspire.OpenAI\Aspire.OpenAI.csproj" />
</ItemGroup>

<Import Project="..\Aspire.OpenAI\MEAIPackageOverrides.targets" />

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.OpenAI;
using Azure.AI.OpenAI;
using Microsoft.Extensions.Hosting;

namespace Aspire.Azure.AI.OpenAI;

/// <summary>
/// A builder for configuring an <see cref="AzureOpenAIClient"/> service registration.
/// </summary>
public class AspireAzureOpenAIClientBuilder : AspireOpenAIClientBuilder
{
/// <summary>
/// Constructs a new instance of <see cref="AspireAzureOpenAIClientBuilder"/>.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostApplicationBuilder"/> with which services are being registered.</param>
/// <param name="connectionName">The name used to retrieve the connection string from the ConnectionStrings configuration section.</param>
/// <param name="serviceKey">The service key used to register the <see cref="AzureOpenAIClient"/> service, if any.</param>
public AspireAzureOpenAIClientBuilder(IHostApplicationBuilder hostBuilder, string connectionName, string? serviceKey)
: base(hostBuilder, connectionName, serviceKey)
{
}

/// <inheritdoc />
public override string ConfigurationSectionName => AspireAzureOpenAIExtensions.DefaultConfigSectionName;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace Microsoft.Extensions.Hosting;
/// </summary>
public static class AspireAzureOpenAIExtensions
{
private const string DefaultConfigSectionName = "Aspire:Azure:AI:OpenAI";
internal const string DefaultConfigSectionName = "Aspire:Azure:AI:OpenAI";

/// <summary>
/// Registers <see cref="AzureOpenAIClient"/> as a singleton in the services provided by the <paramref name="builder"/>.
Expand All @@ -33,8 +33,9 @@ public static class AspireAzureOpenAIExtensions
/// <param name="connectionName">A name used to retrieve the connection string from the ConnectionStrings configuration section.</param>
/// <param name="configureSettings">An optional method that can be used for customizing the <see cref="AzureOpenAISettings"/>. It's invoked after the settings are read from the configuration.</param>
/// <param name="configureClientBuilder">An optional method that can be used for customizing the <see cref="IAzureClientBuilder{AzureOpenAIClient, AzureOpenAIClientOptions}"/>.</param>
/// <returns>An <see cref="AspireAzureOpenAIClientBuilder"/> that can be used to register additional services.</returns>
/// <remarks>Reads the configuration from "Aspire.Azure.AI.OpenAI" section.</remarks>
public static void AddAzureOpenAIClient(
public static AspireAzureOpenAIClientBuilder AddAzureOpenAIClient(
this IHostApplicationBuilder builder,
string connectionName,
Action<AzureOpenAISettings>? configureSettings = null,
Expand All @@ -44,6 +45,8 @@ public static void AddAzureOpenAIClient(

// Add the AzureOpenAIClient service as OpenAIClient. That way the service can be resolved by both service Types.
builder.Services.TryAddSingleton(typeof(OpenAIClient), static provider => provider.GetRequiredService<AzureOpenAIClient>());

return new AspireAzureOpenAIClientBuilder(builder, connectionName, serviceKey: null);
}

/// <summary>
Expand All @@ -55,8 +58,9 @@ public static void AddAzureOpenAIClient(
/// <param name="name">The name of the component, which is used as the <see cref="ServiceDescriptor.ServiceKey"/> of the service and also to retrieve the connection string from the ConnectionStrings configuration section.</param>
/// <param name="configureSettings">An optional method that can be used for customizing the <see cref="AzureOpenAISettings"/>. It's invoked after the settings are read from the configuration.</param>
/// <param name="configureClientBuilder">An optional method that can be used for customizing the <see cref="IAzureClientBuilder{AzureOpenAIClient, OpenAIClientOptions}"/>.</param>
/// <returns>An <see cref="AspireAzureOpenAIClientBuilder"/> that can be used to register additional services.</returns>
/// <remarks>Reads the configuration from "Aspire.Azure.AI.OpenAI:{name}" section.</remarks>
public static void AddKeyedAzureOpenAIClient(
public static AspireAzureOpenAIClientBuilder AddKeyedAzureOpenAIClient(
this IHostApplicationBuilder builder,
string name,
Action<AzureOpenAISettings>? configureSettings = null,
Expand All @@ -68,6 +72,8 @@ public static void AddKeyedAzureOpenAIClient(

// Add the AzureOpenAIClient service as OpenAIClient. That way the service can be resolved by both service Types.
builder.Services.TryAddKeyedSingleton(typeof(OpenAIClient), serviceKey: name, static (provider, key) => provider.GetRequiredKeyedService<AzureOpenAIClient>(key));

return new AspireAzureOpenAIClientBuilder(builder, name, name);
}

private sealed class OpenAIComponent : AzureComponent<AzureOpenAISettings, AzureOpenAIClient, AzureOpenAIClientOptions>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#nullable enable
Aspire.Azure.AI.OpenAI.AspireAzureOpenAIClientBuilder
Aspire.Azure.AI.OpenAI.AspireAzureOpenAIClientBuilder.AspireAzureOpenAIClientBuilder(Microsoft.Extensions.Hosting.IHostApplicationBuilder! hostBuilder, string! connectionName, string? serviceKey) -> void
Aspire.Azure.AI.OpenAI.AzureOpenAISettings
Aspire.Azure.AI.OpenAI.AzureOpenAISettings.AzureOpenAISettings() -> void
Aspire.Azure.AI.OpenAI.AzureOpenAISettings.Credential.get -> Azure.Core.TokenCredential?
Expand All @@ -13,7 +15,8 @@ Aspire.Azure.AI.OpenAI.AzureOpenAISettings.Key.get -> string?
Aspire.Azure.AI.OpenAI.AzureOpenAISettings.Key.set -> void
Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions
Microsoft.Extensions.Hosting.AspireConfigurableOpenAIExtensions
static Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions.AddAzureOpenAIClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Action<Aspire.Azure.AI.OpenAI.AzureOpenAISettings!>? configureSettings = null, System.Action<Azure.Core.Extensions.IAzureClientBuilder<Azure.AI.OpenAI.AzureOpenAIClient!, Azure.AI.OpenAI.AzureOpenAIClientOptions!>!>? configureClientBuilder = null) -> void
static Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions.AddKeyedAzureOpenAIClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Action<Aspire.Azure.AI.OpenAI.AzureOpenAISettings!>? configureSettings = null, System.Action<Azure.Core.Extensions.IAzureClientBuilder<Azure.AI.OpenAI.AzureOpenAIClient!, Azure.AI.OpenAI.AzureOpenAIClientOptions!>!>? configureClientBuilder = null) -> void
override Aspire.Azure.AI.OpenAI.AspireAzureOpenAIClientBuilder.ConfigurationSectionName.get -> string!
static Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions.AddAzureOpenAIClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName, System.Action<Aspire.Azure.AI.OpenAI.AzureOpenAISettings!>? configureSettings = null, System.Action<Azure.Core.Extensions.IAzureClientBuilder<Azure.AI.OpenAI.AzureOpenAIClient!, Azure.AI.OpenAI.AzureOpenAIClientOptions!>!>? configureClientBuilder = null) -> Aspire.Azure.AI.OpenAI.AspireAzureOpenAIClientBuilder!
static Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions.AddKeyedAzureOpenAIClient(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name, System.Action<Aspire.Azure.AI.OpenAI.AzureOpenAISettings!>? configureSettings = null, System.Action<Azure.Core.Extensions.IAzureClientBuilder<Azure.AI.OpenAI.AzureOpenAIClient!, Azure.AI.OpenAI.AzureOpenAIClientOptions!>!>? configureClientBuilder = null) -> Aspire.Azure.AI.OpenAI.AspireAzureOpenAIClientBuilder!
static Microsoft.Extensions.Hosting.AspireConfigurableOpenAIExtensions.AddKeyedOpenAIClientFromConfiguration(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! name) -> void
static Microsoft.Extensions.Hosting.AspireConfigurableOpenAIExtensions.AddOpenAIClientFromConfiguration(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, string! connectionName) -> void
5 changes: 4 additions & 1 deletion src/Components/Aspire.OpenAI/Aspire.OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="OpenAI" />
<PackageReference Include="Microsoft.Extensions.AI" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
</ItemGroup>

<Import Project="MEAIPackageOverrides.targets" />

</Project>
46 changes: 46 additions & 0 deletions src/Components/Aspire.OpenAI/AspireOpenAIClientBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.Hosting;
using OpenAI;

namespace Aspire.OpenAI;

/// <summary>
/// A builder for configuring an <see cref="OpenAIClient"/> service registration.
/// </summary>
public class AspireOpenAIClientBuilder
{
/// <summary>
/// Constructs a new instance of <see cref="AspireOpenAIClientBuilder"/>.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostApplicationBuilder"/> with which services are being registered.</param>
/// <param name="connectionName">The name used to retrieve the connection string from the ConnectionStrings configuration section.</param>
/// <param name="serviceKey">The service key used to register the <see cref="OpenAIClient"/> service, if any.</param>
public AspireOpenAIClientBuilder(IHostApplicationBuilder hostBuilder, string connectionName, string? serviceKey)
{
HostBuilder = hostBuilder;
ConnectionName = connectionName;
ServiceKey = serviceKey;
}

/// <summary>
/// Gets the <see cref="IHostApplicationBuilder"/> with which services are being registered.
/// </summary>
public IHostApplicationBuilder HostBuilder { get; }

/// <summary>
/// Gets the name used to retrieve the connection string from the ConnectionStrings configuration section.
/// </summary>
public string ConnectionName { get; }

/// <summary>
/// Gets the service key used to register the <see cref="OpenAIClient"/> service, if any.
/// </summary>
public string? ServiceKey { get; }

/// <summary>
/// Gets the name of the configuration section for this component type.
/// </summary>
public virtual string ConfigurationSectionName => AspireOpenAIExtensions.DefaultConfigSectionName;
}
Loading
Loading