Skip to content

Commit a99e891

Browse files
Add Support for Azure OpenAI (#33)
Co-authored-by: Tomer Aberbach <[email protected]>
1 parent 1c63b4b commit a99e891

File tree

18 files changed

+529
-13
lines changed

18 files changed

+529
-13
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
plugins {
2+
`java-library`
3+
}
4+
5+
repositories {
6+
gradlePluginPortal()
7+
}
8+
9+
dependencies {
10+
api(project(":openai-java-client-okhttp"))
11+
api("com.azure:azure-identity:1.14.0")
12+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.openai.azure.examples;
2+
3+
import com.openai.azure.credential.AzureApiKeyCredential;
4+
import com.openai.client.OpenAIClient;
5+
import com.openai.client.okhttp.OpenAIOkHttpClient;
6+
import com.openai.core.JsonValue;
7+
import com.openai.models.ChatCompletion;
8+
import com.openai.models.ChatCompletionCreateParams;
9+
import com.openai.models.ChatCompletionMessageParam;
10+
import com.openai.models.ChatCompletionUserMessageParam;
11+
import java.util.List;
12+
13+
/**
14+
* This example demonstrates how to use the Azure API key to authenticate with the OpenAI API.
15+
*/
16+
public final class AzureApiKeyExample {
17+
private AzureApiKeyExample() {}
18+
19+
public static void main(String[] args) {
20+
OpenAIOkHttpClient.Builder clientBuilder = OpenAIOkHttpClient.builder();
21+
22+
/* Azure-specific code starts here */
23+
// You can either set 'endpoint' or 'apiKey' directly in the builder.
24+
// or set same two env vars and use fromEnv() method instead
25+
clientBuilder
26+
.baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT"))
27+
.credential(AzureApiKeyCredential.create(System.getenv("AZURE_OPENAI_KEY")));
28+
/* Azure-specific code ends here */
29+
30+
// All code from this line down is general-purpose OpenAI code
31+
OpenAIClient client = clientBuilder.build();
32+
33+
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
34+
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
35+
ChatCompletionUserMessageParam.builder()
36+
.role(ChatCompletionUserMessageParam.Role.USER)
37+
.content(ChatCompletionUserMessageParam.Content.ofTextContent("Who won the world series in 2020?"))
38+
.build()))
39+
.model("gpt-4o")
40+
.build();
41+
42+
ChatCompletion chatCompletion = client.chat().completions().create(params);
43+
44+
List<ChatCompletion.Choice> choices = chatCompletion.choices();
45+
for (ChatCompletion.Choice choice : choices) {
46+
System.out.println("Choice content: " + choice.message().content().get());
47+
}
48+
49+
JsonValue filterResult = chatCompletion._additionalProperties().get("prompt_filter_results");
50+
System.out.println("Content filter results: " + filterResult);
51+
}
52+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.openai.azure.examples;
2+
3+
import com.openai.azure.credential.AzureApiKeyCredential;
4+
import com.openai.client.OpenAIClientAsync;
5+
import com.openai.client.okhttp.OpenAIOkHttpClientAsync;
6+
import com.openai.core.JsonValue;
7+
import com.openai.models.ChatCompletion;
8+
import com.openai.models.ChatCompletionCreateParams;
9+
import com.openai.models.ChatCompletionMessageParam;
10+
import com.openai.models.ChatCompletionUserMessageParam;
11+
import java.util.List;
12+
13+
/**
14+
* This example demonstrates how to use the Azure API key to authenticate with the OpenAI API, asynchronously.
15+
*/
16+
public final class AzureApiKeyExampleAsync {
17+
18+
private AzureApiKeyExampleAsync() {}
19+
20+
public static void main(String[] args) {
21+
OpenAIOkHttpClientAsync.Builder asyncClientBuilder = OpenAIOkHttpClientAsync.builder();
22+
23+
/* Azure-specific code starts here */
24+
// You can either set 'endpoint' or 'apiKey' directly in the builder.
25+
// or set same two env vars and use fromEnv() method instead
26+
asyncClientBuilder
27+
.baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT"))
28+
.credential(AzureApiKeyCredential.create(System.getenv("AZURE_OPENAI_KEY")));
29+
/* Azure-specific code ends here */
30+
31+
// All code from this line down is general-purpose OpenAI code
32+
OpenAIClientAsync client = asyncClientBuilder.build();
33+
34+
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
35+
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
36+
ChatCompletionUserMessageParam.builder()
37+
.role(ChatCompletionUserMessageParam.Role.USER)
38+
.content(ChatCompletionUserMessageParam.Content.ofTextContent("Who won the world series in 2020?"))
39+
.build()))
40+
.model("gpt-4o")
41+
.build();
42+
43+
ChatCompletion chatCompletion =
44+
client.chat().completions().create(params).join();
45+
46+
List<ChatCompletion.Choice> choices = chatCompletion.choices();
47+
for (ChatCompletion.Choice choice : choices) {
48+
System.out.println("Choice content: " + choice.message().content().get());
49+
}
50+
51+
JsonValue filterResult = chatCompletion._additionalProperties().get("prompt_filter_results");
52+
System.out.println("Content filter results: " + filterResult);
53+
}
54+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.openai.azure.examples;
2+
3+
import com.azure.identity.AuthenticationUtil;
4+
import com.azure.identity.DefaultAzureCredentialBuilder;
5+
import com.openai.client.OpenAIClient;
6+
import com.openai.client.okhttp.OpenAIOkHttpClient;
7+
import com.openai.core.JsonValue;
8+
import com.openai.credential.BearerTokenCredential;
9+
import com.openai.models.ChatCompletion;
10+
import com.openai.models.ChatCompletionCreateParams;
11+
import com.openai.models.ChatCompletionMessageParam;
12+
import com.openai.models.ChatCompletionUserMessageParam;
13+
import java.util.List;
14+
15+
/**
16+
* This example demonstrates how to use the Azure Entra ID to authenticate with the OpenAI API.
17+
*/
18+
public final class AzureEntraIDExample {
19+
20+
private AzureEntraIDExample() {}
21+
22+
public static void main(String[] args) {
23+
OpenAIOkHttpClient.Builder clientBuilder = OpenAIOkHttpClient.builder();
24+
25+
/* Azure-specific code starts here */
26+
// You can either set 'endpoint' directly in the builder.
27+
// or set the env var "AZURE_OPENAI_ENDPOINT" and use fromEnv() method instead
28+
clientBuilder
29+
.baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT"))
30+
.credential(BearerTokenCredential.create(
31+
AuthenticationUtil.getBearerTokenSupplier(
32+
new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default")
33+
));
34+
/* Azure-specific code ends here */
35+
36+
// All code from this line down is general-purpose OpenAI code
37+
OpenAIClient client = clientBuilder.build();
38+
39+
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
40+
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
41+
ChatCompletionUserMessageParam.builder()
42+
.role(ChatCompletionUserMessageParam.Role.USER)
43+
.content(ChatCompletionUserMessageParam.Content.ofTextContent("Who won the world series in 2020?"))
44+
.build()))
45+
.model("gpt-4o")
46+
.build();
47+
48+
ChatCompletion chatCompletion = client.chat().completions().create(params);
49+
50+
List<ChatCompletion.Choice> choices = chatCompletion.choices();
51+
for (ChatCompletion.Choice choice : choices) {
52+
System.out.println("Choice content: " + choice.message().content().get());
53+
}
54+
55+
JsonValue filterResult = chatCompletion._additionalProperties().get("prompt_filter_results");
56+
System.out.println("Content filter results: " + filterResult);
57+
}
58+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.openai.azure.examples;
2+
3+
import com.azure.identity.AuthenticationUtil;
4+
import com.azure.identity.DefaultAzureCredentialBuilder;
5+
import com.openai.client.OpenAIClientAsync;
6+
import com.openai.client.okhttp.OpenAIOkHttpClientAsync;
7+
import com.openai.core.JsonValue;
8+
import com.openai.credential.BearerTokenCredential;
9+
import com.openai.models.ChatCompletion;
10+
import com.openai.models.ChatCompletionCreateParams;
11+
import com.openai.models.ChatCompletionMessageParam;
12+
import com.openai.models.ChatCompletionUserMessageParam;
13+
import java.util.List;
14+
15+
/**
16+
* This example demonstrates how to use the Azure Entra ID to authenticate with the OpenAI API, asynchronously.
17+
*/
18+
public final class AzureEntraIDExampleAsync {
19+
20+
private AzureEntraIDExampleAsync() {}
21+
22+
public static void main(String[] args) {
23+
OpenAIOkHttpClientAsync.Builder asyncClientBuilder = OpenAIOkHttpClientAsync.builder();
24+
25+
/* Azure-specific code starts here */
26+
// You can either set 'endpoint' directly in the builder.
27+
// or set the env var "AZURE_OPENAI_ENDPOINT" and use fromEnv() method instead
28+
asyncClientBuilder
29+
.baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT"))
30+
.credential(BearerTokenCredential.create(
31+
AuthenticationUtil.getBearerTokenSupplier(
32+
new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default")
33+
));
34+
/* Azure-specific code ends here */
35+
36+
// All code from this line down is general-purpose OpenAI code
37+
OpenAIClientAsync asyncClient = asyncClientBuilder.build();
38+
39+
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
40+
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
41+
ChatCompletionUserMessageParam.builder()
42+
.role(ChatCompletionUserMessageParam.Role.USER)
43+
.content(ChatCompletionUserMessageParam.Content.ofTextContent("Who won the world series in 2020?"))
44+
.build()))
45+
.model("gpt-4o")
46+
.build();
47+
48+
ChatCompletion chatCompletion =
49+
asyncClient.chat().completions().create(params).join();
50+
51+
List<ChatCompletion.Choice> choices = chatCompletion.choices();
52+
for (ChatCompletion.Choice choice : choices) {
53+
System.out.println("Choice content: " + choice.message().content().get());
54+
}
55+
56+
JsonValue filterResult = chatCompletion._additionalProperties().get("prompt_filter_results");
57+
System.out.println("Content filter results: " + filterResult);
58+
}
59+
}

openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package com.openai.client.okhttp
44

55
import com.fasterxml.jackson.databind.json.JsonMapper
6+
import com.openai.azure.AzureOpenAIServiceVersion
67
import com.openai.client.OpenAIClient
78
import com.openai.client.OpenAIClientImpl
89
import com.openai.core.ClientOptions
910
import com.openai.core.http.Headers
1011
import com.openai.core.http.QueryParams
12+
import com.openai.credential.Credential
1113
import java.net.Proxy
1214
import java.time.Clock
1315
import java.time.Duration
@@ -130,6 +132,12 @@ class OpenAIOkHttpClient private constructor() {
130132

131133
fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }
132134

135+
fun credential(credential: Credential) = apply { clientOptions.credential(credential) }
136+
137+
fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply {
138+
clientOptions.azureServiceVersion(azureServiceVersion)
139+
}
140+
133141
fun organization(organization: String?) = apply { clientOptions.organization(organization) }
134142

135143
fun project(project: String?) = apply { clientOptions.project(project) }

openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package com.openai.client.okhttp
44

55
import com.fasterxml.jackson.databind.json.JsonMapper
6+
import com.openai.azure.AzureOpenAIServiceVersion
67
import com.openai.client.OpenAIClientAsync
78
import com.openai.client.OpenAIClientAsyncImpl
89
import com.openai.core.ClientOptions
910
import com.openai.core.http.Headers
1011
import com.openai.core.http.QueryParams
12+
import com.openai.credential.Credential
1113
import java.net.Proxy
1214
import java.time.Clock
1315
import java.time.Duration
@@ -130,6 +132,12 @@ class OpenAIOkHttpClientAsync private constructor() {
130132

131133
fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }
132134

135+
fun credential(credential: Credential) = apply { clientOptions.credential(credential) }
136+
137+
fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply {
138+
clientOptions.azureServiceVersion(azureServiceVersion)
139+
}
140+
133141
fun organization(organization: String?) = apply { clientOptions.organization(organization) }
134142

135143
fun project(project: String?) = apply { clientOptions.project(project) }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.openai.azure
2+
3+
import java.util.concurrent.ConcurrentHashMap
4+
5+
class AzureOpenAIServiceVersion private constructor(@get:JvmName("value") val value: String) {
6+
7+
companion object {
8+
private val values: ConcurrentHashMap<String, AzureOpenAIServiceVersion> =
9+
ConcurrentHashMap()
10+
11+
@JvmStatic
12+
fun fromString(version: String): AzureOpenAIServiceVersion =
13+
values.computeIfAbsent(version) { AzureOpenAIServiceVersion(version) }
14+
15+
@JvmStatic val V2022_12_01 = fromString("2022-12-01")
16+
@JvmStatic val V2023_05_15 = fromString("2023-05-15")
17+
@JvmStatic val V2024_02_01 = fromString("2024-02-01")
18+
@JvmStatic val V2024_06_01 = fromString("2024-06-01")
19+
@JvmStatic val V2023_06_01_PREVIEW = fromString("2023-06-01-preview")
20+
@JvmStatic val V2023_07_01_PREVIEW = fromString("2023-07-01-preview")
21+
@JvmStatic val V2024_02_15_PREVIEW = fromString("2024-02-15-preview")
22+
@JvmStatic val V2024_03_01_PREVIEW = fromString("2024-03-01-preview")
23+
@JvmStatic val V2024_04_01_PREVIEW = fromString("2024-04-01-preview")
24+
@JvmStatic val V2024_05_01_PREVIEW = fromString("2024-05-01-preview")
25+
@JvmStatic val V2024_07_01_PREVIEW = fromString("2024-07-01-preview")
26+
@JvmStatic val V2024_08_01_PREVIEW = fromString("2024-08-01-preview")
27+
@JvmStatic val V2024_09_01_PREVIEW = fromString("2024-09-01-preview")
28+
}
29+
30+
override fun equals(other: Any?): Boolean =
31+
this === other || (other is AzureOpenAIServiceVersion && value == other.value)
32+
33+
override fun hashCode(): Int = value.hashCode()
34+
35+
override fun toString(): String = "AzureOpenAIServiceVersion{value=$value}"
36+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.openai.azure.credential
2+
3+
import com.openai.credential.Credential
4+
5+
/** A credential that provides an Azure API key. */
6+
class AzureApiKeyCredential private constructor(private var apiKey: String) : Credential {
7+
8+
init {
9+
validateApiKey(apiKey)
10+
}
11+
12+
companion object {
13+
@JvmStatic fun create(apiKey: String): Credential = AzureApiKeyCredential(apiKey)
14+
15+
private fun validateApiKey(apiKey: String) {
16+
require(apiKey.isNotEmpty()) { "Azure API key cannot be empty." }
17+
}
18+
}
19+
20+
fun apiKey(): String = apiKey
21+
22+
fun update(apiKey: String) = apply {
23+
validateApiKey(apiKey)
24+
this.apiKey = apiKey
25+
}
26+
}

0 commit comments

Comments
 (0)