|
| 1 | +# Migration Guide from Azure OpenAI Java SDK to OpenAI Java SDK |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The [Azure OpenAI Java SDK's latest release][latest_aoai_sdk_release] is the last in the current shape of the SDK. Currently, we recommend users to make use of the [OpenAI Java SDK][openai_java], which can be used to access Azure OpenAI services too. We are currently working on providing classes and types to more easily interact with the service. On the meantime please use this migration guide to access the latest features of OpenAI. |
| 6 | + |
| 7 | +## Client Instantiation & Authentication |
| 8 | + |
| 9 | +### Project setup |
| 10 | + |
| 11 | +Replace the import to `azure-ai-openai` with any of the [dependency import](https://github.com/openai/openai-java?tab=readme-ov-file#installation) that suits your setup. |
| 12 | + |
| 13 | +### Authentication |
| 14 | + |
| 15 | +#### OpenAI |
| 16 | + |
| 17 | +For authenticating a client for OpenAI usage: |
| 18 | + |
| 19 | +```java |
| 20 | +OpenAIClient client = OpenAIOkHttpClient.builder() |
| 21 | + .credential(BearerTokenCredential.create(System.getenv("OPENAI_KEY"))) |
| 22 | + .build(); |
| 23 | +``` |
| 24 | + |
| 25 | +#### Azure OpenAI Entra ID |
| 26 | + |
| 27 | +Make sure you have correctly included in your project [Azure Identity][azure_identity] and your [Azure OpenAI][azure_openai_access] endpoint, then: |
| 28 | + |
| 29 | +```java |
| 30 | +OpenAIClient client = OpenAIOkHttpClient.builder() |
| 31 | + .baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT")) |
| 32 | + // Set the Azure Entra ID |
| 33 | + .credential(BearerTokenCredential.create(AuthenticationUtil.getBearerTokenSupplier( |
| 34 | + new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default"))) |
| 35 | + .build(); |
| 36 | +``` |
| 37 | + |
| 38 | +#### Azure key authentication |
| 39 | + |
| 40 | +For this mode of authentication you will need your service `key` and `endpoint`. Check your access to your [Azure OpenAI resource][azure_openai_access]: |
| 41 | + |
| 42 | +```java |
| 43 | +OpenAIClient client = OpenAIOkHttpClient.builder() |
| 44 | + .baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT")) |
| 45 | + .credential(AzureApiKeyCredential.create(System.getenv("AZURE_OPENAI_KEY"))) |
| 46 | + .build(); |
| 47 | +``` |
| 48 | + |
| 49 | +### Azure OpenAI Service version pinning |
| 50 | + |
| 51 | +To supply the version of the service you want to use of the Azure OpenAI service, do the following: |
| 52 | + |
| 53 | +```java |
| 54 | +import com.openai.azure.AzureOpenAIServiceVersion; |
| 55 | +import com.openai.client.okhttp.OpenAIOkHttpClient; |
| 56 | + |
| 57 | +OpenAIOkHttpClient.Builder clientBuilder = new OpenAIOkHttpClient.Builder() |
| 58 | + .azureServiceVersion(AzureOpenAIServiceVersion.latestStableVersion()) |
| 59 | + // continue your client setup normally from this point onwards |
| 60 | +``` |
| 61 | + |
| 62 | +> [!NOTE] If the `AzureOpenAIServiceVersion` is omitted in the `ClientBuilder` setup, the latest review version in the [OpenAI Java SDK] will be used by default. |
| 63 | +
|
| 64 | +### Sync & Async client |
| 65 | + |
| 66 | +In order to use the To use the sync client: |
| 67 | + |
| 68 | +```java |
| 69 | +OpenAIClient client = OpenAIOkHttpClient.builder() |
| 70 | + .baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT")) |
| 71 | + .credential(AzureApiKeyCredential.create(System.getenv("AZURE_OPENAI_KEY"))) |
| 72 | + .build(); |
| 73 | +``` |
| 74 | + |
| 75 | +And for async: |
| 76 | + |
| 77 | +```java |
| 78 | +OpenAIClientAsync client = OpenAIOkHttpClientAsync.builder() |
| 79 | + .baseUrl(System.getenv("AZURE_OPENAI_ENDPOINT")) |
| 80 | + .credential(AzureApiKeyCredential.create(System.getenv("AZURE_OPENAI_KEY"))) |
| 81 | + .build(); |
| 82 | +``` |
| 83 | + |
| 84 | +One notable difference between the async Azure OpenAI Java SDK and the OpenAI SDK, is that the former uses [reactor](https://projectreactor.io/) and the latter [CompletableFutures](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html). |
| 85 | + |
| 86 | +## Using the SDK |
| 87 | + |
| 88 | +While using the [OpenAI Java SDK][openai_java], all the officially supported fields for the [OpenAI service][openai_openapi] should be supported. So for the latest features, please visit their documentation as it becomes available. |
| 89 | + |
| 90 | +For fields that are "Azure-specific", we will provide guidelines on how to work with available types in the interim. We are planning on having better typed support in the near future. |
| 91 | + |
| 92 | +Every [OpenAI Java SDK][openai_java] type has `Map<String, JsonValue>` named `additionalProperties` in which all fields that are not exclusively [OpenAI service related][openai_openapi] are populated. All the fields that are Azure OpenAI specific (i.e. content filter, Azure AI Search, etc.) will be populated in the aforementioned `Map<String, JsonValue>`. Next, you will find what we believe to be the more common scenarios, and how to provide/access these Azure OpenAI specific fields, while working with the [OpenAI Java SDK][openai_java]. |
| 93 | + |
| 94 | +> [!TIP] |
| 95 | +> We strongly recommend keeping an eye on our `samples` and `test` folders. |
| 96 | +> We use those tests to regularly validate the Azure OpenAI service. |
| 97 | +
|
| 98 | +### OYD: Chat Completions |
| 99 | + |
| 100 | +To supply your Azure AI Search fields, you should setup your request as follows: |
| 101 | + |
| 102 | +```java |
| 103 | +OpenAIClient client = createClient(apiType, apiVersion); |
| 104 | + ChatCompletionCreateParams params = createParamsBuilder(testModel) |
| 105 | + .messages(asList(createSystemMessageParam(), |
| 106 | + createUserMessageParam("<your_prompt>"))) |
| 107 | + .additionalBodyProperties(createExtraBodyForByod()) |
| 108 | + .build(); |
| 109 | + ChatCompletion completion = client.chat().completions().create(params); |
| 110 | + |
| 111 | +// Then the definition for the method where the Azure fields are added: |
| 112 | +Map<String, JsonValue> createExtraBodyForByod() { |
| 113 | + Map<String, JsonValue> authentication = new HashMap<>(); |
| 114 | + authentication.put("type", JsonValue.from("api_key")); |
| 115 | + authentication.put("key", JsonValue.from(System.getenv("AZURE_SEARCH_API_KEY"))); |
| 116 | + |
| 117 | + Map<String, JsonValue> parameters = new HashMap<>(); |
| 118 | + parameters.put("endpoint", JsonValue.from(System.getenv("AZURE_SEARCH_ENDPOINT"))); |
| 119 | + parameters.put("index_name", JsonValue.from(System.getenv("AZURE_OPENAI_SEARCH_INDEX"))); |
| 120 | + parameters.put("authentication", JsonValue.from(authentication)); |
| 121 | + parameters.put("fields_mapping", JsonValue.from(Collections.singletonMap("title_field", "title"))); |
| 122 | + parameters.put("query_type", JsonValue.from("simple")); |
| 123 | + |
| 124 | + Map<String, JsonValue> dataSource = new HashMap<>(); |
| 125 | + dataSource.put("type", JsonValue.from("azure_search")); |
| 126 | + dataSource.put("parameters", JsonValue.from(parameters)); |
| 127 | + |
| 128 | + Map<String, JsonValue> extraBody = new HashMap<>(); |
| 129 | + extraBody.put("data_sources", JsonValue.from(asList(dataSource))); |
| 130 | + return extraBody; |
| 131 | +} |
| 132 | +``` |
| 133 | + |
| 134 | +In order to access the returned Azure fields you can do the following: |
| 135 | + |
| 136 | +```java |
| 137 | +Map<String, JsonValue> additionalProperties = choice.message()._additionalProperties(); |
| 138 | + |
| 139 | +assertTrue(additionalProperties.containsKey("end_turn")); |
| 140 | + |
| 141 | +assertTrue(additionalProperties.containsKey("context")); |
| 142 | +Map<String, JsonValue> context = (Map<String, JsonValue>) additionalProperties.get("context").asObject().get(); |
| 143 | +assertNotNull(context); |
| 144 | +assertTrue(context.containsKey("intent")); |
| 145 | +assertFalse(CoreUtils.isNullOrEmpty((String) context.get("intent").asString().get())); |
| 146 | +assertTrue(context.containsKey("citations")); |
| 147 | +assertTrue(((List<JsonValue>) context.get("citations").asArray().get()).size() > 0); |
| 148 | +``` |
| 149 | + |
| 150 | +For more graceful handling, we suggest using `computeIfPresent` or `getOrDefault`. |
| 151 | + |
| 152 | +### Streaming Chat Completions |
| 153 | + |
| 154 | +For streaming with a sync client you can now do the following: |
| 155 | + |
| 156 | +```java |
| 157 | +OpenAIClient client = createClient(apiType, apiVersion); |
| 158 | + |
| 159 | +StringBuilder responseBuilder = new StringBuilder(); |
| 160 | +try (StreamResponse<ChatCompletionChunk> streamChunks = client.chat().completions().createStreaming( |
| 161 | + createChatCompletionParams(testModel, "Tell a story about a cat and a dog who are best friends. " |
| 162 | + + "It should be at least 100 words long, but at most 120. Be strict about these limits."))) { |
| 163 | + streamChunks.stream() |
| 164 | + .map(it -> (!it.choices().isEmpty()) ? it.choices().get(0).delta().content().orElse("") : "") |
| 165 | + .forEach(System.out::print); |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +Of in an async scenario: |
| 170 | + |
| 171 | +```java |
| 172 | +OpenAIClientAsync client = //... |
| 173 | + |
| 174 | +client.chat() |
| 175 | + .completions() |
| 176 | + .createStreaming(createChatCompletionParams(testModel, |
| 177 | + "Tell a story about a cat and a dog who are best friends. " |
| 178 | + + "It should be at least 100 words long, but at most 120. Be strict about these limits.")) |
| 179 | + .subscribe(it -> { |
| 180 | + String delta = !it.choices().isEmpty() ? it.choices().get(0).delta().content().orElse("") : ""; |
| 181 | + System.out.print(delta); |
| 182 | + }); |
| 183 | +``` |
| 184 | + |
| 185 | +### Responses |
| 186 | + |
| 187 | +To use `Responses` with the [OpenAI Java SDK][openai_java], you can do the following: |
| 188 | + |
| 189 | +```java |
| 190 | +OpenAIClient client = // ... |
| 191 | +client.responses().create(...) |
| 192 | +``` |
| 193 | + |
| 194 | +### Assistants |
| 195 | + |
| 196 | +`Assistants` is still a beta feature. If you want to use it with the [OpenAI Java SDK][openai_java], you can do the following: |
| 197 | + |
| 198 | +```java |
| 199 | +OpenAIClient client = // ... |
| 200 | +client.beta().assistants().create(...) |
| 201 | +``` |
| 202 | + |
| 203 | + |
| 204 | +## Issues |
| 205 | + |
| 206 | +For issues with the [OpenAI Java SDK][openai_java], please follow the guidelines in the repository for opening issues. |
| 207 | + |
| 208 | +<!-- LINKS --> |
| 209 | +[azure_identity]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/identity/azure-identity |
| 210 | +[azure_openai_access]: https://learn.microsoft.com/azure/cognitive-services/openai/overview#how-do-i-get-access-to-azure-openai |
| 211 | +[openai_java]: https://github.com/openai/openai-java |
| 212 | +[openai_openapi]: https://github.com/openai/openai-openapi |
| 213 | +[latest_aoai_sdk_release]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/openai/azure-ai-openai/CHANGELOG.md#100-beta16-2025-03-26 |
0 commit comments