+
4.0.0
@@ -28,6 +29,16 @@
com.azure
azure-search-documents
+
+
+
+ com.azure
+ azure-core-serializer-json-jackson
+
+
org.mockito
diff --git a/java/extensions/semantickernel-settings-loader/pom.xml b/java/extensions/semantickernel-settings-loader/pom.xml
index 68052d2d15..08363ff41a 100644
--- a/java/extensions/semantickernel-settings-loader/pom.xml
+++ b/java/extensions/semantickernel-settings-loader/pom.xml
@@ -28,5 +28,10 @@
org.junit.jupiter
junit-jupiter
+
+ com.azure
+ azure-identity
+ true
+
diff --git a/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureCredentialType.java b/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureCredentialType.java
new file mode 100644
index 0000000000..ba903a6530
--- /dev/null
+++ b/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureCredentialType.java
@@ -0,0 +1,97 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.connectors.ai.openai.util;
+
+import com.azure.core.credential.TokenCredential;
+import com.microsoft.semantickernel.exceptions.ConfigurationException;
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.slf4j.Logger;
+
+/** Defines the type of azure credential used for authenticating to this resource */
+public enum AzureCredentialType {
+ KEY {
+ @Nullable
+ @Override
+ public TokenCredential getTokenCredential() {
+ return null;
+ }
+ },
+ MANAGED_IDENTITY {
+ @Nullable
+ @Override
+ public TokenCredential getTokenCredential() {
+ if (classIsPresent("com.azure.identity.ManagedIdentityCredentialBuilder")) {
+ return new com.azure.identity.ManagedIdentityCredentialBuilder().build();
+ }
+ return null;
+ }
+ },
+ AZURE_CLI {
+ @Nullable
+ @Override
+ public TokenCredential getTokenCredential() {
+ if (classIsPresent("com.azure.identity.AzureCliCredentialBuilder")) {
+ return new com.azure.identity.AzureCliCredentialBuilder().build();
+ }
+ return null;
+ }
+ };
+
+ private static final Logger LOGGER =
+ org.slf4j.LoggerFactory.getLogger(AzureCredentialType.class);
+
+ /**
+ * Create a TokenCredential instance.
+ *
+ * @return TokenCredential instance or null if not found
+ */
+ @Nullable
+ public abstract TokenCredential getTokenCredential();
+
+ /**
+ * Create a TokenCredential instance, uses the default settings locations (see {@link
+ * SettingsMap} for details).
+ *
+ * Set the 'client.azureopenai.credentialtype' environment variable within settings to
+ * determine the token type. {@link AzureCredentialType}
+ *
+ * @return TokenCredential instance or null if not found
+ */
+ @Nullable
+ public static TokenCredential getCredential() throws ConfigurationException {
+ return getCredential((List) null);
+ }
+
+ /**
+ * Create a TokenCredential instance, uses the provided settings locations (see {@link
+ * SettingsMap} for details).
+ *
+ * Set the 'client.azureopenai.credentialtype' environment variable within settings to
+ * determine the token type. {@link AzureCredentialType}
+ *
+ * @return TokenCredential instance or null if not found
+ */
+ @Nullable
+ public static TokenCredential getCredential(@Nullable List propertyFileLocations)
+ throws ConfigurationException {
+ Map settings = OpenAIClientProvider.getSettings(propertyFileLocations);
+ return new AzureOpenAISettings(settings)
+ .getAzureOpenAiCredentialsType()
+ .getTokenCredential();
+ }
+
+ private static boolean classIsPresent(String className) {
+ try {
+ Class.forName(className, false, Thread.currentThread().getContextClassLoader());
+
+ return true;
+ } catch (ClassNotFoundException e) {
+ LOGGER.warn(
+ "Requested Managed Identity or Azure CLI authentication, however the"
+ + " azure-identity library is not available on the classpath");
+ return false;
+ }
+ }
+}
diff --git a/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureOpenAISettings.java b/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureOpenAISettings.java
index 789f129cb9..d2c2409a24 100644
--- a/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureOpenAISettings.java
+++ b/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/AzureOpenAISettings.java
@@ -7,15 +7,18 @@ import javax.annotation.Nullable;
/** Settings for Azure OpenAI client */
public class AzureOpenAISettings extends AbstractOpenAIClientSettings {
+
private static final String DEFAULT_SETTINGS_PREFIX = "client.azureopenai";
private static final String AZURE_OPEN_AI_ENDPOINT_SUFFIX = "endpoint";
private static final String AZURE_OPEN_AI_DEPLOYMENT_NAME_SUFFIX = "deploymentname";
+ private static final String AZURE_OPEN_AI_CREDENTIALS_TYPE = "credentialtype";
@Nullable private final String key;
@Nullable private final String endpoint;
@Nullable private final String deploymentName;
+ @Nullable private final AzureCredentialType credentialType;
private final String settingsPrefix;
public AzureOpenAISettings(Map settings) {
@@ -27,6 +30,12 @@ public class AzureOpenAISettings extends AbstractOpenAIClientSettings {
key = settings.get(settingsPrefix + "." + KEY_SUFFIX);
endpoint = settings.get(settingsPrefix + "." + AZURE_OPEN_AI_ENDPOINT_SUFFIX);
deploymentName = settings.get(settingsPrefix + "." + AZURE_OPEN_AI_DEPLOYMENT_NAME_SUFFIX);
+
+ String credType =
+ settings.getOrDefault(settingsPrefix + "." + AZURE_OPEN_AI_CREDENTIALS_TYPE, "key")
+ .toUpperCase();
+
+ credentialType = AzureCredentialType.valueOf(credType);
}
/**
@@ -65,6 +74,10 @@ public class AzureOpenAISettings extends AbstractOpenAIClientSettings {
return key;
}
+ public AzureCredentialType getAzureOpenAiCredentialsType() {
+ return credentialType;
+ }
+
@Override
public boolean assertIsValid() throws ConfigurationException {
if (key == null) {
diff --git a/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/OpenAIClientProvider.java b/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/OpenAIClientProvider.java
index 0904b8cc92..95015d3320 100644
--- a/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/OpenAIClientProvider.java
+++ b/java/extensions/semantickernel-settings-loader/src/main/java/com/microsoft/semantickernel/connectors/ai/openai/util/OpenAIClientProvider.java
@@ -7,6 +7,7 @@ import com.azure.ai.openai.OpenAIAsyncClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.KeyCredential;
+import com.azure.core.credential.TokenCredential;
import com.azure.core.util.ClientOptions;
import com.microsoft.semantickernel.SemanticKernelHttpSettings;
import com.microsoft.semantickernel.exceptions.ConfigurationException;
@@ -79,7 +80,7 @@ public class OpenAIClientProvider {
return new OpenAIClientProvider(settings, clientType);
}
- private static Map getSettings(@Nullable List propertyFileLocations)
+ static Map getSettings(@Nullable List propertyFileLocations)
throws ConfigurationException {
Map settings;
@@ -235,19 +236,27 @@ public class OpenAIClientProvider {
private OpenAIAsyncClient buildAzureOpenAIClient() throws ConfigurationException {
AzureOpenAISettings settings = new AzureOpenAISettings(configuredSettings);
- try {
- settings.assertIsValid();
- } catch (ConfigurationException e) {
- LOGGER.warn("Could not instantiate Azure OpenAI client");
- LOGGER.warn(e.getMessage());
- throw e;
+ OpenAIClientBuilder builder = new OpenAIClientBuilder().endpoint(settings.getEndpoint());
+
+ TokenCredential tokenCredential = null;
+
+ if (settings.getAzureOpenAiCredentialsType() != null) {
+ tokenCredential = settings.getAzureOpenAiCredentialsType().getTokenCredential();
}
- OpenAIClientBuilder builder =
- new OpenAIClientBuilder()
- .endpoint(settings.getEndpoint())
- .clientOptions(clientOptions)
- .credential(new AzureKeyCredential(settings.getKey()));
+ if (tokenCredential != null) {
+ builder.credential(tokenCredential);
+ } else {
+ try {
+ settings.assertIsValid();
+ } catch (ConfigurationException e) {
+ LOGGER.warn("Could not instantiate Azure OpenAI client");
+ LOGGER.warn(e.getMessage());
+ throw e;
+ }
+
+ builder.credential(new AzureKeyCredential(settings.getKey()));
+ }
return builder.buildAsyncClient();
}
diff --git a/java/samples/sample-code/README.md b/java/samples/sample-code/README.md
index 56c951268b..afb693b24f 100644
--- a/java/samples/sample-code/README.md
+++ b/java/samples/sample-code/README.md
@@ -34,10 +34,14 @@ They can then be run by:
# Configuration
-You can define the provider of Open AI by setting the `OPENAI_CLIENT_TYPE`
+By default, the samples will use the Open AI client, but you can also use the Azure Open AI client.
+
+## Open AI client type
+
+You can define the provider of Open AI (openai.com or Azure), this can be done by setting the `OPENAI_CLIENT_TYPE`
property or environment variable to either [`OPENAI`](https://openai.com/api/)
-or [`AZURE_OPEN_AI`](https://learn.microsoft.com/azure/cognitive-services/openai/).
-By default, the samples will use the Open AI client.
+or [`AZURE_OPEN_AI`](https://learn.microsoft.com/azure/cognitive-services/openai/). By default, the samples will use the
+Open AI client.
```shell
OPENAI_CLIENT_TYPE=OPEN_AI ../../mvnw exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
@@ -55,7 +59,6 @@ The samples search for the client settings in the following order:
1. Properties file at `java/samples/conf.properties`.
1. Properties file at `~/.sk/conf.properties`.
-
## Properties File
You can set the location of a properties file, by setting the `CONF_PROPERTIES` property or environment variable, ie:
@@ -123,3 +126,15 @@ OPEN_AI_KEY="my-key" \
OPEN_AI_ORGANIZATION_ID="organisation id" \
../../mvnw clean package exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
```
+
+## Managed Identity and Azure CLI Credentials (Azure only)
+
+The `credentialtype` setting (i.e `client.openai.credentialtype`):
+
+- `managed_identity`: will attempt to obtain credentials via Azure's managed identity.
+- `azure_cli`: will attempt to obtain credentials via Azure's CLI, to use this you must have logged in to the az client
+ for instance by running `az login`.
+
+To use either of these settings, the `azure-identity` dependency must be available to the application. Additionally, you
+must have the "Cognitive Services User" role assigned to your identity.
+
diff --git a/java/samples/sample-code/pom.xml b/java/samples/sample-code/pom.xml
index fcef47df95..a833c3d59a 100644
--- a/java/samples/sample-code/pom.xml
+++ b/java/samples/sample-code/pom.xml
@@ -76,6 +76,10 @@
com.microsoft.semantic-kernel
semantickernel-settings-loader