Skip to content

Commit 57cbc93

Browse files
dlucadrianwyatt
andauthored
API simplification: make AI service ID optional (#744)
### Motivation and Context When configuring AI models (text completion, embeddings, chat, image generation, etc) currently one has to pass a "service Id", ie assign a name to the configuration. This allows apps to work with different models providing the same features, e.g. working with OpenAI and Azure OpenAI and HuggingFace at the same time. In many scenarios though, there is only one AI model/service provider, e.g. an app might be using just Open AI, and in this case service names are unused. Also, the kernel config API allows to remove AI models from the configuration, a tentative feature that has never been used and can be removed for now, and reintroduced if we find scenarios or get explicit requests. ### Description * Make the Service ID optional, making the AI configuration simpler * Remove some unused API, reducing the complexity of kernel config Co-authored-by: Adrian Bonar (HE/HIM) <[email protected]>
1 parent 0c31da2 commit 57cbc93

37 files changed

+232
-551
lines changed

dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIClientBase.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ protected OpenAIClientBase(
2828
string apiKey,
2929
string? organization = null,
3030
IDelegatingHandlerFactory? handlerFactory = null,
31-
ILogger? log = null
32-
)
31+
ILogger? log = null)
3332
{
3433
Verify.NotEmpty(modelId, "The Model Id cannot be empty");
3534
this.ModelId = modelId;

dotnet/src/Connectors/Connectors.AI.OpenAI/KernelConfigOpenAIExtensions.cs

Lines changed: 33 additions & 54 deletions
Large diffs are not rendered by default.

dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/OpenAITextCompletion.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ public OpenAITextCompletion(
2828
string apiKey,
2929
string? organization = null,
3030
IDelegatingHandlerFactory? handlerFactory = null,
31-
ILogger? log = null
32-
) : base(modelId, apiKey, organization, handlerFactory, log)
31+
ILogger? log = null) : base(modelId, apiKey, organization, handlerFactory, log)
3332
{
3433
}
3534

Lines changed: 46 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3-
using System.Linq;
43
using Microsoft.SemanticKernel;
5-
using Moq;
64
using Xunit;
75

86
namespace SemanticKernel.Connectors.UnitTests.OpenAI;
@@ -16,8 +14,8 @@ public class KernelConfigOpenAIExtensionsTests
1614
public void ItSucceedsWhenAddingDifferentServiceTypeWithSameId()
1715
{
1816
var target = new KernelConfig();
19-
target.AddAzureTextCompletionService("azure", "depl", "https://url", "key");
20-
target.AddAzureTextEmbeddingGenerationService("azure", "depl2", "https://url", "key");
17+
target.AddAzureTextCompletionService("depl", "https://url", "key", serviceId: "azure");
18+
target.AddAzureTextEmbeddingGenerationService("depl2", "https://url", "key", serviceId: "azure");
2119

2220
Assert.True(target.TextCompletionServices.ContainsKey("azure"));
2321
Assert.True(target.TextEmbeddingGenerationServices.ContainsKey("azure"));
@@ -28,10 +26,10 @@ public void ItTellsIfAServiceIsAvailable()
2826
{
2927
// Arrange
3028
var target = new KernelConfig();
31-
target.AddAzureTextCompletionService("azure", "depl", "https://url", "key");
32-
target.AddOpenAITextCompletionService("oai", "model", "apikey");
33-
target.AddAzureTextEmbeddingGenerationService("azure", "depl2", "https://url2", "key");
34-
target.AddOpenAITextEmbeddingGenerationService("oai2", "model2", "apikey2");
29+
target.AddAzureTextCompletionService("deployment1", "https://url", "key", serviceId: "azure");
30+
target.AddOpenAITextCompletionService("model", "apikey", serviceId: "oai");
31+
target.AddAzureTextEmbeddingGenerationService("deployment2", "https://url2", "key", serviceId: "azure");
32+
target.AddOpenAITextEmbeddingGenerationService("model2", "apikey2", serviceId: "oai2");
3533

3634
// Assert
3735
Assert.True(target.TextCompletionServices.ContainsKey("azure"));
@@ -52,219 +50,80 @@ public void ItCanOverwriteServices()
5250
var target = new KernelConfig();
5351

5452
// Act - Assert no exception occurs
55-
target.AddAzureTextCompletionService("one", "dep", "https://localhost", "key");
56-
target.AddAzureTextCompletionService("one", "dep", "https://localhost", "key");
57-
target.AddOpenAITextCompletionService("one", "model", "key");
58-
target.AddOpenAITextCompletionService("one", "model", "key");
59-
target.AddAzureTextEmbeddingGenerationService("one", "dep", "https://localhost", "key");
60-
target.AddAzureTextEmbeddingGenerationService("one", "dep", "https://localhost", "key");
61-
target.AddOpenAITextEmbeddingGenerationService("one", "model", "key");
62-
target.AddOpenAITextEmbeddingGenerationService("one", "model", "key");
53+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
54+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
55+
target.AddOpenAITextCompletionService("model", "key", serviceId: "one");
56+
target.AddOpenAITextCompletionService("model", "key", serviceId: "one");
57+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "one");
58+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "one");
59+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "one");
60+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "one");
6361
}
6462

6563
[Fact]
6664
public void ItCanRemoveAllServices()
6765
{
6866
// Arrange
6967
var target = new KernelConfig();
70-
target.AddAzureTextCompletionService("one", "dep", "https://localhost", "key");
71-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
72-
target.AddOpenAITextCompletionService("3", "model", "key");
73-
target.AddOpenAITextCompletionService("4", "model", "key");
74-
target.AddAzureTextEmbeddingGenerationService("5", "dep", "https://localhost", "key");
75-
target.AddAzureTextEmbeddingGenerationService("6", "dep", "https://localhost", "key");
76-
target.AddOpenAITextEmbeddingGenerationService("7", "model", "key");
77-
target.AddOpenAITextEmbeddingGenerationService("8", "model", "key");
68+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
69+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "2");
70+
target.AddOpenAITextCompletionService("model", "key", serviceId: "3");
71+
target.AddOpenAITextCompletionService("model", "key", serviceId: "4");
72+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "5");
73+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "6");
74+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "7");
75+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "8");
7876

7977
// Act
8078
target.RemoveAllTextCompletionServices();
8179
target.RemoveAllTextEmbeddingGenerationServices();
8280

8381
// Assert
84-
Assert.Empty(target.AllTextEmbeddingGenerationServiceIds);
85-
Assert.Empty(target.AllTextCompletionServiceIds);
82+
Assert.Empty(target.TextEmbeddingGenerationServices);
83+
Assert.Empty(target.TextCompletionServices);
8684
}
8785

8886
[Fact]
8987
public void ItCanRemoveAllTextCompletionServices()
9088
{
9189
// Arrange
9290
var target = new KernelConfig();
93-
target.AddAzureTextCompletionService("one", "dep", "https://localhost", "key");
94-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
95-
target.AddOpenAITextCompletionService("3", "model", "key");
96-
target.AddOpenAITextCompletionService("4", "model", "key");
97-
target.AddAzureTextEmbeddingGenerationService("5", "dep", "https://localhost", "key");
98-
target.AddAzureTextEmbeddingGenerationService("6", "dep", "https://localhost", "key");
99-
target.AddOpenAITextEmbeddingGenerationService("7", "model", "key");
100-
target.AddOpenAITextEmbeddingGenerationService("8", "model", "key");
91+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
92+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "2");
93+
target.AddOpenAITextCompletionService("model", "key", serviceId: "3");
94+
target.AddOpenAITextCompletionService("model", "key", serviceId: "4");
95+
96+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "5");
97+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "6");
98+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "7");
99+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "8");
101100

102101
// Act
103102
target.RemoveAllTextCompletionServices();
104103

105-
// Assert
106-
Assert.Equal(4, target.AllTextEmbeddingGenerationServiceIds.Count());
107-
Assert.Empty(target.AllTextCompletionServiceIds);
104+
// Assert (+1 for the default)
105+
Assert.Equal(4 + 1, target.TextEmbeddingGenerationServices.Count);
108106
}
109107

110108
[Fact]
111109
public void ItCanRemoveAllTextEmbeddingGenerationServices()
112110
{
113111
// Arrange
114112
var target = new KernelConfig();
115-
target.AddAzureTextCompletionService("one", "dep", "https://localhost", "key");
116-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
117-
target.AddOpenAITextCompletionService("3", "model", "key");
118-
target.AddOpenAITextCompletionService("4", "model", "key");
119-
target.AddAzureTextEmbeddingGenerationService("5", "dep", "https://localhost", "key");
120-
target.AddAzureTextEmbeddingGenerationService("6", "dep", "https://localhost", "key");
121-
target.AddOpenAITextEmbeddingGenerationService("7", "model", "key");
122-
target.AddOpenAITextEmbeddingGenerationService("8", "model", "key");
113+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "one");
114+
target.AddAzureTextCompletionService("dep", "https://localhost", "key", serviceId: "2");
115+
target.AddOpenAITextCompletionService("model", "key", serviceId: "3");
116+
target.AddOpenAITextCompletionService("model", "key", serviceId: "4");
117+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "5");
118+
target.AddAzureTextEmbeddingGenerationService("dep", "https://localhost", "key", serviceId: "6");
119+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "7");
120+
target.AddOpenAITextEmbeddingGenerationService("model", "key", serviceId: "8");
123121

124122
// Act
125123
target.RemoveAllTextEmbeddingGenerationServices();
126124

127-
// Assert
128-
Assert.Equal(4, target.AllTextCompletionServiceIds.Count());
129-
Assert.Empty(target.AllTextEmbeddingGenerationServiceIds);
130-
}
131-
132-
[Fact]
133-
public void ItCanRemoveOneCompletionService()
134-
{
135-
// Arrange
136-
var target = new KernelConfig();
137-
target.AddAzureTextCompletionService("1", "dep", "https://localhost", "key");
138-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
139-
target.AddOpenAITextCompletionService("3", "model", "key");
140-
Assert.Equal("1", target.DefaultTextCompletionServiceId);
141-
142-
// Act - Assert
143-
target.RemoveTextCompletionService("1");
144-
Assert.Equal("2", target.DefaultTextCompletionServiceId);
145-
target.RemoveTextCompletionService("2");
146-
Assert.Equal("3", target.DefaultTextCompletionServiceId);
147-
target.RemoveTextCompletionService("3");
148-
Assert.Null(target.DefaultTextCompletionServiceId);
149-
}
150-
151-
[Fact]
152-
public void ItCanRemoveOneTextEmbeddingGenerationService()
153-
{
154-
// Arrange
155-
var target = new KernelConfig();
156-
target.AddAzureTextEmbeddingGenerationService("1", "dep", "https://localhost", "key");
157-
target.AddAzureTextEmbeddingGenerationService("2", "dep", "https://localhost", "key");
158-
target.AddOpenAITextEmbeddingGenerationService("3", "model", "key");
159-
Assert.Equal("1", target.DefaultTextEmbeddingGenerationServiceId);
160-
161-
// Act - Assert
162-
target.RemoveTextEmbeddingGenerationService("1");
163-
Assert.Equal("2", target.DefaultTextEmbeddingGenerationServiceId);
164-
target.RemoveTextEmbeddingGenerationService("2");
165-
Assert.Equal("3", target.DefaultTextEmbeddingGenerationServiceId);
166-
target.RemoveTextEmbeddingGenerationService("3");
167-
Assert.Null(target.DefaultTextEmbeddingGenerationServiceId);
168-
}
169-
170-
[Fact]
171-
public void GetTextEmbeddingGenerationServiceItReturnsDefaultWhenNonExistingIdIsProvided()
172-
{
173-
// Arrange
174-
var target = new KernelConfig();
175-
target.AddOpenAITextEmbeddingGenerationService("1", "dep", "https://localhost", "key");
176-
target.AddAzureTextEmbeddingGenerationService("2", "dep", "https://localhost", "key");
177-
target.SetDefaultTextEmbeddingGenerationService("2");
178-
179-
// Act
180-
var result = target.GetTextEmbeddingGenerationServiceIdOrDefault("test");
181-
182-
// Assert
183-
Assert.Equal("2", result);
184-
}
185-
186-
[Fact]
187-
public void GetEmbeddingServiceReturnsSpecificWhenExistingIdIsProvided()
188-
{
189-
// Arrange
190-
var kernel = new Mock<IKernel>();
191-
var target = new KernelConfig();
192-
target.AddOpenAITextEmbeddingGenerationService("1", "dep", "https://localhost", "key");
193-
target.AddAzureTextEmbeddingGenerationService("2", "dep", "https://localhost", "key");
194-
target.SetDefaultTextEmbeddingGenerationService("2");
195-
196-
// Act
197-
var result = target.GetTextEmbeddingGenerationServiceIdOrDefault("1");
198-
199-
// Assert
200-
Assert.Equal("1", result);
201-
}
202-
203-
[Fact]
204-
public void GetEmbeddingServiceReturnsDefaultWhenNoIdIsProvided()
205-
{
206-
// Arrange
207-
var kernel = new Mock<IKernel>();
208-
var target = new KernelConfig();
209-
target.AddOpenAITextEmbeddingGenerationService("1", "dep", "https://localhost", "key");
210-
target.AddAzureTextEmbeddingGenerationService("2", "dep", "https://localhost", "key");
211-
target.SetDefaultTextEmbeddingGenerationService("2");
212-
213-
// Act
214-
var result = target.GetTextEmbeddingGenerationServiceIdOrDefault();
215-
216-
// Assert
217-
Assert.Equal("2", result);
218-
}
219-
220-
[Fact]
221-
public void GetTextCompletionServiceReturnsDefaultWhenNonExistingIdIsProvided()
222-
{
223-
// Arrange
224-
var kernel = new Mock<IKernel>();
225-
var target = new KernelConfig();
226-
target.AddOpenAITextCompletionService("1", "dep", "https://localhost", "key");
227-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
228-
target.SetDefaultTextCompletionService("2");
229-
230-
// Act
231-
var result = target.GetTextCompletionServiceIdOrDefault("345");
232-
233-
// Assert
234-
Assert.Equal("2", result);
235-
}
236-
237-
[Fact]
238-
public void GetTextCompletionServiceReturnsSpecificWhenExistingIdIsProvided()
239-
{
240-
// Arrange
241-
var kernel = new Mock<IKernel>();
242-
var target = new KernelConfig();
243-
target.AddOpenAITextCompletionService("1", "dep", "https://localhost", "key");
244-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
245-
target.SetDefaultTextCompletionService("2");
246-
247-
// Act
248-
var result = target.GetTextCompletionServiceIdOrDefault("1");
249-
250-
// Assert
251-
Assert.Equal("1", result);
252-
}
253-
254-
[Fact]
255-
public void GetTextCompletionServiceItReturnsDefaultWhenNoIdIsProvided()
256-
{
257-
// Arrange
258-
var kernel = new Mock<IKernel>();
259-
var target = new KernelConfig();
260-
target.AddOpenAITextCompletionService("1", "dep", "https://localhost", "key");
261-
target.AddAzureTextCompletionService("2", "dep", "https://localhost", "key");
262-
target.SetDefaultTextCompletionService("2");
263-
264-
// Act
265-
var result = target.GetTextCompletionServiceIdOrDefault();
266-
267-
// Assert
268-
Assert.Equal("2", result);
125+
// Assert (+1 for the default)
126+
Assert.Equal(4 + 1, target.TextCompletionServices.Count);
127+
Assert.Empty(target.TextEmbeddingGenerationServices);
269128
}
270129
}

dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ public async Task OpenAIHttpRetryPolicyTestAsync(string prompt, string expectedO
9292

9393
// Use an invalid API key to force a 401 Unauthorized response
9494
target.Config.AddOpenAITextCompletionService(
95-
serviceId: openAIConfiguration.ServiceId,
9695
modelId: openAIConfiguration.ModelId,
9796
apiKey: "INVALID_KEY");
9897

@@ -116,7 +115,6 @@ public async Task OpenAIHttpInvalidKeyShouldReturnErrorDetailAsync()
116115

117116
// Use an invalid API key to force a 401 Unauthorized response
118117
target.Config.AddOpenAITextCompletionService(
119-
serviceId: openAIConfiguration.ServiceId,
120118
modelId: openAIConfiguration.ModelId,
121119
apiKey: "INVALID_KEY");
122120

@@ -142,7 +140,6 @@ public async Task AzureOpenAIHttpInvalidKeyShouldReturnErrorDetailAsync()
142140
Assert.NotNull(azureOpenAIConfiguration);
143141

144142
target.Config.AddAzureTextCompletionService(
145-
serviceId: azureOpenAIConfiguration.ServiceId,
146143
deploymentName: azureOpenAIConfiguration.DeploymentName,
147144
endpoint: azureOpenAIConfiguration.Endpoint,
148145
apiKey: "INVALID_KEY");
@@ -169,7 +166,6 @@ public async Task AzureOpenAIHttpExceededMaxTokensShouldReturnErrorDetailAsync()
169166
Assert.NotNull(azureOpenAIConfiguration);
170167

171168
target.Config.AddAzureTextCompletionService(
172-
serviceId: azureOpenAIConfiguration.ServiceId,
173169
deploymentName: azureOpenAIConfiguration.DeploymentName,
174170
endpoint: azureOpenAIConfiguration.Endpoint,
175171
apiKey: azureOpenAIConfiguration.ApiKey);
@@ -247,9 +243,9 @@ private void ConfigureOpenAI(IKernel kernel)
247243
Assert.NotNull(openAIConfiguration);
248244

249245
kernel.Config.AddOpenAITextCompletionService(
250-
serviceId: openAIConfiguration.ServiceId,
251246
modelId: openAIConfiguration.ModelId,
252-
apiKey: openAIConfiguration.ApiKey);
247+
apiKey: openAIConfiguration.ApiKey,
248+
serviceId: openAIConfiguration.ServiceId);
253249

254250
kernel.Config.SetDefaultTextCompletionService(openAIConfiguration.ServiceId);
255251
}
@@ -261,10 +257,10 @@ private void ConfigureAzureOpenAI(IKernel kernel)
261257
Assert.NotNull(azureOpenAIConfiguration);
262258

263259
kernel.Config.AddAzureTextCompletionService(
264-
serviceId: azureOpenAIConfiguration.ServiceId,
265260
deploymentName: azureOpenAIConfiguration.DeploymentName,
266261
endpoint: azureOpenAIConfiguration.Endpoint,
267-
apiKey: azureOpenAIConfiguration.ApiKey);
262+
apiKey: azureOpenAIConfiguration.ApiKey,
263+
serviceId: azureOpenAIConfiguration.ServiceId);
268264

269265
kernel.Config.SetDefaultTextCompletionService(azureOpenAIConfiguration.ServiceId);
270266
}

dotnet/src/IntegrationTests/Planning/PlanTests.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,21 +449,17 @@ private IKernel InitializeKernel(bool useEmbeddings = false)
449449
.Configure(config =>
450450
{
451451
config.AddAzureTextCompletionService(
452-
serviceId: azureOpenAIConfiguration.ServiceId,
453452
deploymentName: azureOpenAIConfiguration.DeploymentName,
454453
endpoint: azureOpenAIConfiguration.Endpoint,
455454
apiKey: azureOpenAIConfiguration.ApiKey);
456455

457456
if (useEmbeddings)
458457
{
459458
config.AddAzureTextEmbeddingGenerationService(
460-
serviceId: azureOpenAIEmbeddingsConfiguration.ServiceId,
461459
deploymentName: azureOpenAIEmbeddingsConfiguration.DeploymentName,
462460
endpoint: azureOpenAIEmbeddingsConfiguration.Endpoint,
463461
apiKey: azureOpenAIEmbeddingsConfiguration.ApiKey);
464462
}
465-
466-
config.SetDefaultTextCompletionService(azureOpenAIConfiguration.ServiceId);
467463
});
468464

469465
if (useEmbeddings)

0 commit comments

Comments
 (0)