-
Notifications
You must be signed in to change notification settings - Fork 732
Integration for Azure.AI.Inference
#9103
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
Merged
sebastienros
merged 20 commits into
dotnet:main
from
aaronpowell:aaronpowell/issue-9011
May 12, 2025
Merged
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
cacc720
Initial implementation of Inference SDK as an integration
aaronpowell 5f10191
Making properties public
aaronpowell 3a2cc94
Fixing missed refactor code
aaronpowell 51ac74c
Apply suggestions from code review
aaronpowell f7230ed
Updating from code review feedback
aaronpowell 7f4889d
Merge branch 'main' into aaronpowell/issue-9011
aaronpowell e08b444
Adding tests
aaronpowell efdc6f8
Updating based on feedback
aaronpowell ede53da
Adding readme
aaronpowell 137f02f
Adding tests
aaronpowell 9f9c025
Adding change from feedback
aaronpowell 1357337
Adding change from feedback
aaronpowell c7cf933
Renaming the method to include Azure as that's more consistent with o…
aaronpowell 70ad18f
Merge branch 'main' into aaronpowell/issue-9011
aaronpowell dfbaed1
Fix unit test
sebastienros d7e69de
Fix README inclusion
sebastienros 5e2d407
Tweak AddChatClient APIs to take an optional DeploymentId.
eerhardt 32c281a
Merge branch 'main' into aaronpowell/issue-9011
sebastienros 30fb1ec
Rename argument
sebastienros b19df43
Add unit test for deploymentId
sebastienros File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
src/Components/Aspire.Azure.AI.Inference/Aspire.Azure.AI.Inference.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>$(DefaultTargetFramework)</TargetFramework> | ||
| <IsPackable>true</IsPackable> | ||
| <PackageTags>$(ComponentAzurePackageTags) ai</PackageTags> | ||
| <Description>A client for Azure AI Inference SDK that integrates with Aspire, including logging and telemetry.</Description> | ||
| <PackageIconFullPath>$(SharedDir)Azure_256x.png</PackageIconFullPath> | ||
| <NoWarn>$(NoWarn);SYSLIB1100;SYSLIB1101</NoWarn> | ||
| <!-- In preview until Azure.AI.Inference is shipped stable. --> | ||
| <SuppressFinalPackageVersion>true</SuppressFinalPackageVersion> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="..\Common\AzureComponent.cs" Link="AzureComponent.cs" /> | ||
| <Compile Include="..\Common\ConfigurationSchemaAttributes.cs" Link="ConfigurationSchemaAttributes.cs" /> | ||
sebastienros marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <Compile Include="..\Common\HealthChecksExtensions.cs" Link="HealthChecksExtensions.cs" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Azure.AI.Inference" /> | ||
| <PackageReference Include="Microsoft.Extensions.AI" /> | ||
| <PackageReference Include="Microsoft.Extensions.AI.AzureAIInference" /> | ||
| <PackageReference Include="Microsoft.Extensions.Azure" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" /> | ||
| <PackageReference Include="Microsoft.Extensions.Configuration.Binder" /> | ||
| <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" /> | ||
| <PackageReference Include="OpenTelemetry.Extensions.Hosting" /> | ||
| </ItemGroup> | ||
| </Project> | ||
178 changes: 178 additions & 0 deletions
178
src/Components/Aspire.Azure.AI.Inference/AspireAzureAIInferenceExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Aspire.Azure.Common; | ||
| using Azure; | ||
| using Azure.AI.Inference; | ||
| using Azure.Core; | ||
| using Azure.Core.Extensions; | ||
| using Azure.Identity; | ||
| using Microsoft.Extensions.AI; | ||
| using Microsoft.Extensions.Azure; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
|
|
||
| namespace Microsoft.Extensions.Hosting; | ||
|
|
||
| /// <summary> | ||
| /// Extension methods for adding Azure AI Inference services to an Aspire application. | ||
| /// </summary> | ||
| public static class AspireAzureAIInferenceExtensions | ||
| { | ||
| private const string DefaultConfigSectionName = "Aspire:Azure:AI:Inference"; | ||
|
|
||
| /// <summary> | ||
| /// Adds a <see cref="ChatCompletionsClient"/> to the application and configures it with the specified settings. | ||
| /// </summary> | ||
| /// <param name="builder">The <see cref="IHostApplicationBuilder"/> to add the client to.</param> | ||
| /// <param name="connectionName">The name of the client. This is used to retrieve the connection string from configuration.</param> | ||
| /// <param name="configureClient">An optional callback to configure the <see cref="ChatCompletionsClientSettings"/>.</param> | ||
| /// <param name="configureClientBuilder">An optional callback to configure the <see cref="IAzureClientBuilder{TClient, TOptions}"/> for the client.</param> | ||
| /// <returns>An <see cref="AspireChatCompletionsClientBuilder"/> that can be used to further configure the client.</returns> | ||
| /// <exception cref="InvalidOperationException">Thrown when endpoint is missing from settings.</exception> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// The client is registered as a singleton with a keyed service. | ||
| /// </para> | ||
| /// <para> | ||
| /// Configuration is loaded from the "Aspire:Azure:AI:Inference" section, and can be supplemented with a connection string named after the <paramref name="connectionName"/> parameter. | ||
| /// </para> | ||
| /// </remarks> | ||
| public static AspireChatCompletionsClientBuilder AddChatCompletionsClient( | ||
| this IHostApplicationBuilder builder, | ||
| string connectionName, | ||
| Action<ChatCompletionsClientSettings>? configureClient = null, | ||
|
||
| Action<IAzureClientBuilder<ChatCompletionsClient, AzureAIInferenceClientOptions>>? configureClientBuilder = null) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(builder); | ||
| ArgumentException.ThrowIfNullOrEmpty(connectionName); | ||
|
|
||
| var settings = new ChatCompletionsClientServiceComponent().AddClient( | ||
| builder, | ||
| DefaultConfigSectionName, | ||
| configureClient, | ||
| configureClientBuilder, | ||
| connectionName, | ||
| serviceKey: null); | ||
|
|
||
| return new AspireChatCompletionsClientBuilder(builder, serviceKey: null, settings.DeploymentId, settings.DisableTracing); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Adds a <see cref="ChatCompletionsClient"/> to the application and configures it with the specified settings. | ||
| /// </summary> | ||
| /// <param name="builder">The <see cref="IHostApplicationBuilder"/> to add the client to.</param> | ||
| /// <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="configureClient">An optional callback to configure the <see cref="ChatCompletionsClientSettings"/>.</param> | ||
| /// <param name="configureClientBuilder">An optional callback to configure the <see cref="IAzureClientBuilder{TClient, TOptions}"/> for the client.</param> | ||
| /// <returns>An <see cref="AspireChatCompletionsClientBuilder"/> that can be used to further configure the client.</returns> | ||
| /// <exception cref="InvalidOperationException">Thrown when endpoint is missing from settings.</exception> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// The client is registered as a singleton with a keyed service. | ||
| /// </para> | ||
| /// <para> | ||
| /// Configuration is loaded from the "Aspire:Azure:AI:Inference" section, and can be supplemented with a connection string named after the <paramref name="name"/> parameter. | ||
| /// </para> | ||
| /// </remarks> | ||
| public static AspireChatCompletionsClientBuilder AddKeyedChatCompletionsClient( | ||
| this IHostApplicationBuilder builder, | ||
| string name, | ||
| Action<ChatCompletionsClientSettings>? configureClient = null, | ||
| Action<IAzureClientBuilder<ChatCompletionsClient, AzureAIInferenceClientOptions>>? configureClientBuilder = null) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(builder); | ||
| ArgumentException.ThrowIfNullOrEmpty(name); | ||
|
|
||
| var settings = new ChatCompletionsClientServiceComponent().AddClient( | ||
| builder, | ||
| DefaultConfigSectionName, | ||
| configureClient, | ||
| configureClientBuilder, | ||
| name, | ||
| serviceKey: name); | ||
|
|
||
| return new AspireChatCompletionsClientBuilder(builder, serviceKey: name, settings.DeploymentId, settings.DisableTracing); | ||
| } | ||
|
|
||
| private sealed class ChatCompletionsClientServiceComponent : AzureComponent<ChatCompletionsClientSettings, ChatCompletionsClient, AzureAIInferenceClientOptions> | ||
RussKie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| protected override IAzureClientBuilder<ChatCompletionsClient, AzureAIInferenceClientOptions> AddClient( | ||
| AzureClientFactoryBuilder azureFactoryBuilder, | ||
| ChatCompletionsClientSettings settings, | ||
| string connectionName, string | ||
| configurationSectionName) | ||
| { | ||
| return azureFactoryBuilder.AddClient<ChatCompletionsClient, AzureAIInferenceClientOptions>((options, _, _) => | ||
| { | ||
| if (settings.Endpoint is null) | ||
| { | ||
| throw new InvalidOperationException($"An ChatCompletionsClient could not be configured. Ensure valid connection information was provided in 'ConnectionStrings:{connectionName}' or specify a '{nameof(ChatCompletionsClientSettings.Endpoint)}' or '{nameof(ChatCompletionsClientSettings.Key)}' in the '{configurationSectionName}' configuration section."); | ||
| } | ||
| else | ||
| { | ||
| // Connect to Azure AI Foundry using key auth | ||
| if (!string.IsNullOrEmpty(settings.Key)) | ||
| { | ||
| var credential = new AzureKeyCredential(settings.Key); | ||
| return new ChatCompletionsClient(settings.Endpoint, credential, options); | ||
| } | ||
| else | ||
| { | ||
| return new ChatCompletionsClient(settings.Endpoint, settings.TokenCredential ?? new DefaultAzureCredential(), options); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| protected override void BindClientOptionsToConfiguration(IAzureClientBuilder<ChatCompletionsClient, AzureAIInferenceClientOptions> clientBuilder, IConfiguration configuration) | ||
| { | ||
| #pragma warning disable IDE0200 // Remove unnecessary lambda expression - needed so the ConfigBinder Source Generator works | ||
| clientBuilder.ConfigureOptions(options => configuration.Bind(options)); | ||
| #pragma warning restore IDE0200 | ||
| } | ||
|
|
||
| protected override void BindSettingsToConfiguration(ChatCompletionsClientSettings settings, IConfiguration configuration) | ||
| => configuration.Bind(settings); | ||
|
|
||
| protected override IHealthCheck CreateHealthCheck(ChatCompletionsClient client, ChatCompletionsClientSettings settings) | ||
| => throw new NotImplementedException(); | ||
|
|
||
| protected override bool GetHealthCheckEnabled(ChatCompletionsClientSettings settings) | ||
| => false; | ||
|
|
||
| protected override bool GetMetricsEnabled(ChatCompletionsClientSettings settings) | ||
| => !settings.DisableMetrics; | ||
|
|
||
| protected override TokenCredential? GetTokenCredential(ChatCompletionsClientSettings settings) | ||
| => settings.TokenCredential; | ||
|
|
||
| protected override bool GetTracingEnabled(ChatCompletionsClientSettings settings) | ||
| => !settings.DisableTracing; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a <see cref="IChatClient"/> from the <see cref="ChatCompletionsClient"/> registered in the service collection. | ||
| /// </summary> | ||
| /// <param name="builder"></param> | ||
| /// <returns></returns> | ||
| public static ChatClientBuilder AddChatClient(this AspireChatCompletionsClientBuilder builder) => | ||
danmoseley marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| builder.HostBuilder.Services.AddChatClient(services => | ||
| { | ||
| var chatCompletionsClient = !string.IsNullOrEmpty(builder.ServiceKey) ? | ||
| services.GetRequiredService<ChatCompletionsClient>() : | ||
| services.GetRequiredKeyedService<ChatCompletionsClient>(builder.ServiceKey); | ||
|
|
||
| var result = chatCompletionsClient.AsIChatClient(); | ||
|
|
||
| if (builder.DisableTracing) | ||
| { | ||
| return result; | ||
| } | ||
|
|
||
| return new ChatClientBuilder(result) | ||
danmoseley marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .UseOpenTelemetry() | ||
| .Build(); | ||
| }); | ||
| } | ||
43 changes: 43 additions & 0 deletions
43
src/Components/Aspire.Azure.AI.Inference/AspireChatCompletionsClientBuilder.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Azure.AI.Inference; | ||
|
|
||
| namespace Microsoft.Extensions.Hosting; | ||
|
|
||
| /// <summary> | ||
| /// Provides a builder for configuring and integrating an Aspire Chat Completions client into a host application. | ||
| /// </summary> | ||
| /// <remarks>This class is used to configure the necessary parameters for creating an Aspire Chat Completions | ||
| /// client, such as the host application builder, service key, and optional model ID. It is intended for internal use | ||
| /// within the application setup process.</remarks> | ||
| /// <param name="hostBuilder">The <see cref="IHostApplicationBuilder"/> with which services are being registered.</param> | ||
| /// <param name="serviceKey">The service key used to register the <see cref="ChatCompletionsClient"/> service, if any.</param> | ||
| /// <param name="deploymentId">The id of the deployment in Azure AI Foundry.</param> | ||
| /// <param name="disableTracing">A flag to indicate whether tracing should be disabled.</param> | ||
| public class AspireChatCompletionsClientBuilder( | ||
aaronpowell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| IHostApplicationBuilder hostBuilder, | ||
| string? serviceKey, | ||
| string? deploymentId, | ||
| bool disableTracing) | ||
| { | ||
| /// <summary> | ||
| /// Gets a flag indicating whether tracing should be disabled. | ||
| /// </summary> | ||
| public bool DisableTracing { get; } = disableTracing; | ||
|
|
||
| /// <summary> | ||
| /// Gets the <see cref="IHostApplicationBuilder"/> with which services are being registered. | ||
| /// </summary> | ||
| public IHostApplicationBuilder HostBuilder { get; } = hostBuilder ?? throw new ArgumentNullException(nameof(hostBuilder)); | ||
|
|
||
| /// <summary> | ||
| /// Gets the service key used to register the <see cref="ChatCompletionsClient"/> service, if any. | ||
| /// </summary> | ||
| public string? ServiceKey { get; } = serviceKey; | ||
|
|
||
| /// <summary> | ||
| /// The ID of the deployment in Azure AI Foundry. | ||
| /// </summary> | ||
| public string? DeploymentId { get; } = deploymentId; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does this get used? |
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Aspire; | ||
| using Azure.AI.Inference; | ||
| using Microsoft.Extensions.Hosting; | ||
|
|
||
| [assembly: ConfigurationSchema("Aspire:Azure:AI:Inference", typeof(ChatCompletionsClientSettings))] | ||
| [assembly: ConfigurationSchema("Aspire:Azure:AI:Inference:ClientOptions", typeof(AzureAIInferenceClientOptions))] | ||
|
|
||
| [assembly: LoggingCategories( | ||
| "Azure", | ||
| "Azure.Core", | ||
| "Azure.Identity" | ||
| )] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.