diff --git a/dotnet/src/Connectors/Connectors.Amazon.UnitTests/Extensions/BedrockKernelBuilderExtensionTests.cs b/dotnet/src/Connectors/Connectors.Amazon.UnitTests/Extensions/BedrockKernelBuilderExtensionTests.cs index e1692b4ea218..3f5879d9bdb4 100644 --- a/dotnet/src/Connectors/Connectors.Amazon.UnitTests/Extensions/BedrockKernelBuilderExtensionTests.cs +++ b/dotnet/src/Connectors/Connectors.Amazon.UnitTests/Extensions/BedrockKernelBuilderExtensionTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Amazon.BedrockRuntime; using Amazon.Runtime; using Microsoft.SemanticKernel.ChatCompletion; @@ -18,13 +19,15 @@ public class BedrockKernelBuilderExtensionTests /// /// Checks that AddBedrockTextGenerationService builds a proper kernel with a null bedrockRuntime. /// - [Fact] - public void AddBedrockTextGenerationCreatesServiceWithNonNullBedrockRuntime() + [Theory] + [InlineData("amazon.titan-text-premier-v1:0")] + [InlineData("us.amazon.titan-text-premier-v1:0")] + public void AddBedrockTextGenerationCreatesServiceWithNonNullBedrockRuntime(string modelId) { // Arrange var bedrockRuntime = new Mock().Object; var builder = Kernel.CreateBuilder(); - builder.AddBedrockTextGenerationService("amazon.titan-text-premier-v1:0", bedrockRuntime); + builder.AddBedrockTextGenerationService(modelId, bedrockRuntime); // Act var kernel = builder.Build(); @@ -37,13 +40,15 @@ public void AddBedrockTextGenerationCreatesServiceWithNonNullBedrockRuntime() /// /// Checks that AddBedrockChatCompletionService builds a proper kernel with a non-null bedrockRuntime. /// - [Fact] - public void AddBedrockChatCompletionCreatesServiceWithNonNullBedrockRuntime() + [Theory] + [InlineData("amazon.titan-text-premier-v1:0")] + [InlineData("us.amazon.titan-text-premier-v1:0")] + public void AddBedrockChatCompletionCreatesServiceWithNonNullBedrockRuntime(string modelId) { // Arrange var bedrockRuntime = new Mock().Object; var builder = Kernel.CreateBuilder(); - builder.AddBedrockChatCompletionService("amazon.titan-text-premier-v1:0", bedrockRuntime); + builder.AddBedrockChatCompletionService(modelId, bedrockRuntime); // Act var kernel = builder.Build(); @@ -65,4 +70,22 @@ public void AwsServiceClientBeforeServiceRequestDoesNothingForNonWebServiceReque // Assert // No exceptions should be thrown } + + [Theory] + [InlineData("unknown.titan-text-premier-v1:0")] + [InlineData("us.unknown.titan-text-premier-v1:0")] + public void AwsUnknownBedrockTextCompletionModelShouldThrowException(string modelId) + { + // Arrange + var bedrockRuntime = new Mock().Object; + var builder = Kernel.CreateBuilder(); + builder.AddBedrockTextGenerationService(modelId, bedrockRuntime); + + // Act & Assert + Assert.Throws(() => + { + var kernel = builder.Build(); + kernel.GetRequiredService(); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Amazon/Bedrock/Core/BedrockServiceFactory.cs b/dotnet/src/Connectors/Connectors.Amazon/Bedrock/Core/BedrockServiceFactory.cs index e5b59f0c465a..7bc9178c69c6 100644 --- a/dotnet/src/Connectors/Connectors.Amazon/Bedrock/Core/BedrockServiceFactory.cs +++ b/dotnet/src/Connectors/Connectors.Amazon/Bedrock/Core/BedrockServiceFactory.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Linq; namespace Microsoft.SemanticKernel.Connectors.Amazon.Core; @@ -9,6 +10,30 @@ namespace Microsoft.SemanticKernel.Connectors.Amazon.Core; /// internal sealed class BedrockServiceFactory { + /// + /// Represents an array of region prefixes used to identify different cross-region configurations + /// for service operations. The prefixes correspond to general geographic areas such as + /// "us" (United States), "eu" (Europe), and "apac" (Asia-Pacific). + /// (sourced from https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html) + /// + private static readonly string[] s_crossRegionPrefixes = ["us.", "eu.", "apac."]; + + /// + /// Removes the cross-region prefix from the provided model identifier if it exists. + /// + /// The model identifier, which may contain a cross-region prefix. + /// The model identifier without the cross-region prefix. + private static string ScrubCrossRegionPrefix(string modelId) + { + var prefix = s_crossRegionPrefixes.FirstOrDefault(prefix => modelId.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)); + if (!string.IsNullOrWhiteSpace(prefix)) + { + modelId = modelId.Substring(prefix.Length); + } + + return modelId; + } + /// /// Gets the model service for body conversion. /// @@ -17,7 +42,7 @@ internal sealed class BedrockServiceFactory /// Thrown if provider or model is not supported for text generation. internal IBedrockTextGenerationService CreateTextGenerationService(string modelId) { - (string modelProvider, string modelName) = this.GetModelProviderAndName(modelId); + (string modelProvider, string modelName) = this.GetModelProviderAndName(ScrubCrossRegionPrefix(modelId)); switch (modelProvider.ToUpperInvariant()) { @@ -79,7 +104,7 @@ internal IBedrockTextGenerationService CreateTextGenerationService(string modelI /// Thrown if provider or model is not supported for chat completion. internal IBedrockChatCompletionService CreateChatCompletionService(string modelId) { - (string modelProvider, string modelName) = this.GetModelProviderAndName(modelId); + (string modelProvider, string modelName) = this.GetModelProviderAndName(ScrubCrossRegionPrefix(modelId)); switch (modelProvider.ToUpperInvariant()) {