11using Azure . AI . OpenAI ;
2+ using Azure . Core ;
3+ using Azure . Identity ;
24using EssentialCSharp . Chat . Common . Services ;
35using Microsoft . Extensions . Configuration ;
46using Microsoft . Extensions . DependencyInjection ;
@@ -9,38 +11,52 @@ namespace EssentialCSharp.Chat.Common.Extensions;
911public static class ServiceCollectionExtensions
1012{
1113 /// <summary>
12- /// Adds Azure OpenAI and related AI services to the service collection
14+ /// Adds Azure OpenAI and related AI services to the service collection using Managed Identity
1315 /// </summary>
1416 /// <param name="services">The service collection to add services to</param>
1517 /// <param name="aiOptions">The AI configuration options</param>
1618 /// <param name="postgresConnectionString">The PostgreSQL connection string for the vector store</param>
19+ /// <param name="credential">The token credential to use for authentication. If null, DefaultAzureCredential will be used.</param>
1720 /// <returns>The service collection for chaining</returns>
18- public static IServiceCollection AddAzureOpenAIServices ( this IServiceCollection services , AIOptions aiOptions , string postgresConnectionString )
21+ public static IServiceCollection AddAzureOpenAIServices (
22+ this IServiceCollection services ,
23+ AIOptions aiOptions ,
24+ string postgresConnectionString ,
25+ TokenCredential ? credential = null )
1926 {
20- if ( string . IsNullOrEmpty ( aiOptions . Endpoint ) ||
21- string . IsNullOrEmpty ( aiOptions . ApiKey ) )
22- // Register Azure OpenAI services
27+ // Use DefaultAzureCredential if no credential is provided
28+ // This works both locally (using Azure CLI, Visual Studio, etc.) and in Azure (using Managed Identity)
29+ credential ??= new DefaultAzureCredential ( ) ;
30+
31+ if ( string . IsNullOrEmpty ( aiOptions . Endpoint ) )
32+ {
33+ throw new InvalidOperationException ( "AIOptions.Endpoint is required." ) ;
34+ }
35+
36+ var endpoint = new Uri ( aiOptions . Endpoint ) ;
37+
38+ // Register Azure OpenAI services with Managed Identity authentication
2339#pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
2440 services . AddAzureOpenAIChatClient (
2541 aiOptions . ChatDeploymentName ,
26- aiOptions . Endpoint ,
27- aiOptions . ApiKey ) ;
42+ endpoint . ToString ( ) ,
43+ credential ) ;
2844
2945 services . AddSingleton ( provider =>
30- new AzureOpenAIClient ( new Uri ( aiOptions . Endpoint ) , new Azure . AzureKeyCredential ( aiOptions . ApiKey ) ) ) ;
46+ new AzureOpenAIClient ( endpoint , credential ) ) ;
3147
3248 services . AddAzureOpenAIChatCompletion (
3349 aiOptions . ChatDeploymentName ,
3450 aiOptions . Endpoint ,
35- aiOptions . ApiKey ) ;
51+ credential ) ;
3652
3753 // Add PostgreSQL vector store
3854 services . AddPostgresVectorStore ( postgresConnectionString ) ;
3955
4056 services . AddAzureOpenAIEmbeddingGenerator (
4157 aiOptions . VectorGenerationDeploymentName ,
4258 aiOptions . Endpoint ,
43- aiOptions . ApiKey ) ;
59+ credential ) ;
4460#pragma warning restore SKEXP0010
4561
4662 // Register shared AI services
@@ -57,8 +73,12 @@ public static IServiceCollection AddAzureOpenAIServices(this IServiceCollection
5773 /// </summary>
5874 /// <param name="services">The service collection to add services to</param>
5975 /// <param name="configuration">The configuration to read AIOptions from</param>
76+ /// <param name="credential">Optional token credential to use for authentication. If null, DefaultAzureCredential will be used.</param>
6077 /// <returns>The service collection for chaining</returns>
61- public static IServiceCollection AddAzureOpenAIServices ( this IServiceCollection services , IConfiguration configuration )
78+ public static IServiceCollection AddAzureOpenAIServices (
79+ this IServiceCollection services ,
80+ IConfiguration configuration ,
81+ TokenCredential ? credential = null )
6282 {
6383 // Configure AI options from configuration
6484 services . Configure < AIOptions > ( configuration . GetSection ( "AIOptions" ) ) ;
@@ -73,6 +93,66 @@ public static IServiceCollection AddAzureOpenAIServices(this IServiceCollection
7393 var postgresConnectionString = configuration . GetConnectionString ( "PostgresVectorStore" ) ??
7494 throw new InvalidOperationException ( "Connection string 'PostgresVectorStore' not found." ) ;
7595
76- return services . AddAzureOpenAIServices ( aiOptions , postgresConnectionString ) ;
96+ return services . AddAzureOpenAIServices ( aiOptions , postgresConnectionString , credential ) ;
97+ }
98+
99+ /// <summary>
100+ /// Adds Azure OpenAI and related AI services to the service collection using API key authentication (legacy)
101+ /// </summary>
102+ /// <param name="services">The service collection to add services to</param>
103+ /// <param name="aiOptions">The AI configuration options</param>
104+ /// <param name="postgresConnectionString">The PostgreSQL connection string for the vector store</param>
105+ /// <param name="apiKey">The API key for Azure OpenAI authentication</param>
106+ /// <returns>The service collection for chaining</returns>
107+ [ Obsolete ( "API key authentication is not recommended for production. Use AddAzureOpenAIServices with Managed Identity instead." ) ]
108+ public static IServiceCollection AddAzureOpenAIServicesWithApiKey (
109+ this IServiceCollection services ,
110+ AIOptions aiOptions ,
111+ string postgresConnectionString ,
112+ string apiKey )
113+ {
114+ if ( string . IsNullOrEmpty ( apiKey ) )
115+ {
116+ throw new ArgumentException ( "API key cannot be null or empty." , nameof ( apiKey ) ) ;
117+ }
118+
119+ if ( string . IsNullOrEmpty ( aiOptions . Endpoint ) )
120+ {
121+ throw new InvalidOperationException ( "AIOptions.Endpoint is required." ) ;
122+ }
123+
124+ var endpoint = new Uri ( aiOptions . Endpoint ) ;
125+
126+ // Register Azure OpenAI services with API key authentication
127+ #pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
128+ services . AddAzureOpenAIChatClient (
129+ aiOptions . ChatDeploymentName ,
130+ aiOptions . Endpoint ,
131+ apiKey ) ;
132+
133+ services . AddSingleton ( provider =>
134+ new AzureOpenAIClient ( endpoint , new Azure . AzureKeyCredential ( apiKey ) ) ) ;
135+
136+ services . AddAzureOpenAIChatCompletion (
137+ aiOptions . ChatDeploymentName ,
138+ aiOptions . Endpoint ,
139+ apiKey ) ;
140+
141+ // Add PostgreSQL vector store
142+ services . AddPostgresVectorStore ( postgresConnectionString ) ;
143+
144+ services . AddAzureOpenAIEmbeddingGenerator (
145+ aiOptions . VectorGenerationDeploymentName ,
146+ aiOptions . Endpoint ,
147+ apiKey ) ;
148+ #pragma warning restore SKEXP0010
149+
150+ // Register shared AI services
151+ services . AddSingleton < EmbeddingService > ( ) ;
152+ services . AddSingleton < AISearchService > ( ) ;
153+ services . AddSingleton < AIChatService > ( ) ;
154+ services . AddSingleton < MarkdownChunkingService > ( ) ;
155+
156+ return services ;
77157 }
78158}
0 commit comments