openai/openai-java

Public

mirrored from https://github.com/openai/openai-javaAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.44.2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

README.md

915lines · modecode

1# OpenAI Java API Library
2
3> [!NOTE]
4> The OpenAI Java API Library is currently in _beta_.
5>
6> There may be minor breaking changes.
7>
8> Have thoughts or feedback? [File an issue](https://github.com/openai/openai-java/issues/new) or comment on [this thread](https://community.openai.com/t/your-feedback-requested-java-sdk/1061029).
9
10<!-- x-release-please-start-version -->
11
12[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.44.2)
13[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.44.2/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.44.2)
14
15<!-- x-release-please-end -->
16
17The OpenAI Java SDK provides convenient access to the [OpenAI REST API](https://platform.openai.com/docs) from applications written in Java.
18
19<!-- x-release-please-start-version -->
20
21The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are also available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/0.44.2).
22
23<!-- x-release-please-end -->
24
25## Installation
26
27<!-- x-release-please-start-version -->
28
29### Gradle
30
31```kotlin
32implementation("com.openai:openai-java:0.44.2")
33```
34
35### Maven
36
37```xml
38<dependency>
39 <groupId>com.openai</groupId>
40 <artifactId>openai-java</artifactId>
41 <version>0.44.2</version>
42</dependency>
43```
44
45<!-- x-release-please-end -->
46
47## Requirements
48
49This library requires Java 8 or later.
50
51## Usage
52
53See the [`openai-java-example`](openai-java-example/src/main/java/com/openai/example) directory for complete and runnable examples.
54
55The primary API for interacting with OpenAI models is the [Responses API](https://platform.openai.com/docs/api-reference/responses). You can generate text from the model with the code below.
56
57```java
58import com.openai.client.OpenAIClient;
59import com.openai.client.okhttp.OpenAIOkHttpClient;
60import com.openai.models.ChatModel;
61import com.openai.models.responses.Response;
62import com.openai.models.responses.ResponseCreateParams;
63
64// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
65OpenAIClient client = OpenAIOkHttpClient.fromEnv();
66
67ResponseCreateParams params = ResponseCreateParams.builder()
68 .input("Say this is a test")
69 .model(ChatModel.GPT_4O)
70 .build();
71Response response = client.responses().create(params);
72```
73
74The previous standard (supported indefinitely) for generating text is the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). You can use that API to generate text from the model with the code below.
75
76```java
77import com.openai.client.OpenAIClient;
78import com.openai.client.okhttp.OpenAIOkHttpClient;
79import com.openai.models.ChatModel;
80import com.openai.models.chat.completions.ChatCompletion;
81import com.openai.models.chat.completions.ChatCompletionCreateParams;
82
83// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
84OpenAIClient client = OpenAIOkHttpClient.fromEnv();
85
86ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
87 .addUserMessage("Say this is a test")
88 .model(ChatModel.O3_MINI)
89 .build();
90ChatCompletion chatCompletion = client.chat().completions().create(params);
91```
92
93## Client configuration
94
95Configure the client using environment variables:
96
97```java
98import com.openai.client.OpenAIClient;
99import com.openai.client.okhttp.OpenAIOkHttpClient;
100
101// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
102OpenAIClient client = OpenAIOkHttpClient.fromEnv();
103```
104
105Or manually:
106
107```java
108import com.openai.client.OpenAIClient;
109import com.openai.client.okhttp.OpenAIOkHttpClient;
110
111OpenAIClient client = OpenAIOkHttpClient.builder()
112 .apiKey("My API Key")
113 .build();
114```
115
116Or using a combination of the two approaches:
117
118```java
119import com.openai.client.OpenAIClient;
120import com.openai.client.okhttp.OpenAIOkHttpClient;
121
122OpenAIClient client = OpenAIOkHttpClient.builder()
123 // Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
124 .fromEnv()
125 .apiKey("My API Key")
126 .build();
127```
128
129See this table for the available options:
130
131| Setter | Environment variable | Required | Default value |
132| -------------- | -------------------- | -------- | ------------- |
133| `apiKey` | `OPENAI_API_KEY` | true | - |
134| `organization` | `OPENAI_ORG_ID` | false | - |
135| `project` | `OPENAI_PROJECT_ID` | false | - |
136
137> [!TIP]
138> Don't create more than one client in the same application. Each client has a connection pool and
139> thread pools, which are more efficient to share between requests.
140
141## Requests and responses
142
143To send a request to the OpenAI API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a Java class.
144
145For example, `client.chat().completions().create(...)` should be called with an instance of `ChatCompletionCreateParams`, and it will return an instance of `ChatCompletion`.
146
147## Immutability
148
149Each class in the SDK has an associated [builder](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java) or factory method for constructing it.
150
151Each class is [immutable](https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html) once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy.
152
153Because each class is immutable, builder modification will _never_ affect already built class instances.
154
155## Asynchronous execution
156
157The default client is synchronous. To switch to asynchronous execution, call the `async()` method:
158
159```java
160import com.openai.client.OpenAIClient;
161import com.openai.client.okhttp.OpenAIOkHttpClient;
162import com.openai.models.ChatModel;
163import com.openai.models.chat.completions.ChatCompletion;
164import com.openai.models.chat.completions.ChatCompletionCreateParams;
165import java.util.concurrent.CompletableFuture;
166
167// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
168OpenAIClient client = OpenAIOkHttpClient.fromEnv();
169
170ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
171 .addUserMessage("Say this is a test")
172 .model(ChatModel.O3_MINI)
173 .build();
174CompletableFuture<ChatCompletion> chatCompletion = client.async().chat().completions().create(params);
175```
176
177Or create an asynchronous client from the beginning:
178
179```java
180import com.openai.client.OpenAIClientAsync;
181import com.openai.client.okhttp.OpenAIOkHttpClientAsync;
182import com.openai.models.ChatModel;
183import com.openai.models.chat.completions.ChatCompletion;
184import com.openai.models.chat.completions.ChatCompletionCreateParams;
185import java.util.concurrent.CompletableFuture;
186
187// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
188OpenAIClientAsync client = OpenAIOkHttpClientAsync.fromEnv();
189
190ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
191 .addUserMessage("Say this is a test")
192 .model(ChatModel.O3_MINI)
193 .build();
194CompletableFuture<ChatCompletion> chatCompletion = client.chat().completions().create(params);
195```
196
197The asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s.
198
199## Streaming
200
201The SDK defines methods that return response "chunk" streams, where each chunk can be individually processed as soon as it arrives instead of waiting on the full response. Streaming methods generally correspond to [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) or [JSONL](https://jsonlines.org) responses.
202
203Some of these methods may have streaming and non-streaming variants, but a streaming method will always have a `Streaming` suffix in its name, even if it doesn't have a non-streaming variant.
204
205These streaming methods return [`StreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt) for synchronous clients:
206
207```java
208import com.openai.core.http.StreamResponse;
209import com.openai.models.chat.completions.ChatCompletionChunk;
210
211try (StreamResponse<ChatCompletionChunk> streamResponse = client.chat().completions().createStreaming(params)) {
212 streamResponse.stream().forEach(chunk -> {
213 System.out.println(chunk);
214 });
215 System.out.println("No more chunks!");
216}
217```
218
219Or [`AsyncStreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/AsyncStreamResponse.kt) for asynchronous clients:
220
221```java
222import com.openai.core.http.AsyncStreamResponse;
223import com.openai.models.chat.completions.ChatCompletionChunk;
224import java.util.Optional;
225
226client.async().chat().completions().createStreaming(params).subscribe(chunk -> {
227 System.out.println(chunk);
228});
229
230// If you need to handle errors or completion of the stream
231client.async().chat().completions().createStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() {
232 @Override
233 public void onNext(ChatCompletionChunk chunk) {
234 System.out.println(chunk);
235 }
236
237 @Override
238 public void onComplete(Optional<Throwable> error) {
239 if (error.isPresent()) {
240 System.out.println("Something went wrong!");
241 throw new RuntimeException(error.get());
242 } else {
243 System.out.println("No more chunks!");
244 }
245 }
246});
247
248// Or use futures
249client.async().chat().completions().createStreaming(params)
250 .subscribe(chunk -> {
251 System.out.println(chunk);
252 })
253 .onCompleteFuture();
254 .whenComplete((unused, error) -> {
255 if (error != null) {
256 System.out.println("Something went wrong!");
257 throw new RuntimeException(error);
258 } else {
259 System.out.println("No more chunks!");
260 }
261 });
262```
263
264Async streaming uses a dedicated per-client cached thread pool [`Executor`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) to stream without blocking the current thread. This default is suitable for most purposes.
265
266To use a different `Executor`, configure the subscription using the `executor` parameter:
267
268```java
269import java.util.concurrent.Executor;
270import java.util.concurrent.Executors;
271
272Executor executor = Executors.newFixedThreadPool(4);
273client.async().chat().completions().createStreaming(params).subscribe(
274 chunk -> System.out.println(chunk), executor
275);
276```
277
278Or configure the client globally using the `streamHandlerExecutor` method:
279
280```java
281import com.openai.client.OpenAIClient;
282import com.openai.client.okhttp.OpenAIOkHttpClient;
283import java.util.concurrent.Executors;
284
285OpenAIClient client = OpenAIOkHttpClient.builder()
286 .fromEnv()
287 .streamHandlerExecutor(Executors.newFixedThreadPool(4))
288 .build();
289```
290
291### Streaming helpers
292
293The SDK provides conveniences for streamed chat completions. A
294[`ChatCompletionAccumulator`](openai-java-core/src/main/kotlin/com/openai/helpers/ChatCompletionAccumulator.kt)
295can record the stream of chat completion chunks in the response as they are processed and accumulate
296a [`ChatCompletion`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt)
297object similar to that which would have been returned by the non-streaming API.
298
299For a synchronous response add a
300[`Stream.peek()`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-)
301call to the stream pipeline to accumulate each chunk:
302
303```java
304import com.openai.core.http.StreamResponse;
305import com.openai.helpers.ChatCompletionAccumulator;
306import com.openai.models.chat.completions.ChatCompletion;
307import com.openai.models.chat.completions.ChatCompletionChunk;
308
309ChatCompletionAccumulator chatCompletionAccumulator = ChatCompletionAccumulator.create();
310
311try (StreamResponse<ChatCompletionChunk> streamResponse =
312 client.chat().completions().createStreaming(createParams)) {
313 streamResponse.stream()
314 .peek(chatCompletionAccumulator::accumulate)
315 .flatMap(completion -> completion.choices().stream())
316 .flatMap(choice -> choice.delta().content().stream())
317 .forEach(System.out::print);
318}
319
320ChatCompletion chatCompletion = chatCompletionAccumulator.chatCompletion();
321```
322
323For an asynchronous response, add the `ChatCompletionAccumulator` to the `subscribe()` call:
324
325```java
326import com.openai.helpers.ChatCompletionAccumulator;
327import com.openai.models.chat.completions.ChatCompletion;
328
329ChatCompletionAccumulator chatCompletionAccumulator = ChatCompletionAccumulator.create();
330
331client.chat()
332 .completions()
333 .createStreaming(createParams)
334 .subscribe(chunk -> chatCompletionAccumulator.accumulate(chunk).choices().stream()
335 .flatMap(choice -> choice.delta().content().stream())
336 .forEach(System.out::print))
337 .onCompleteFuture()
338 .join();
339
340ChatCompletion chatCompletion = chatCompletionAccumulator.chatCompletion();
341```
342
343## File uploads
344
345The SDK defines methods that accept files.
346
347To upload a file, pass a [`Path`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html):
348
349```java
350import com.openai.models.files.FileCreateParams;
351import com.openai.models.files.FileObject;
352import com.openai.models.files.FilePurpose;
353import java.nio.file.Paths;
354
355FileCreateParams params = FileCreateParams.builder()
356 .purpose(FilePurpose.FINE_TUNE)
357 .file(Paths.get("input.jsonl"))
358 .build();
359FileObject fileObject = client.files().create(params);
360```
361
362Or an arbitrary [`InputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html):
363
364```java
365import com.openai.models.files.FileCreateParams;
366import com.openai.models.files.FileObject;
367import com.openai.models.files.FilePurpose;
368import java.net.URL;
369
370FileCreateParams params = FileCreateParams.builder()
371 .purpose(FilePurpose.FINE_TUNE)
372 .file(new URL("https://example.com/input.jsonl").openStream())
373 .build();
374FileObject fileObject = client.files().create(params);
375```
376
377Or a `byte[]` array:
378
379```java
380import com.openai.models.files.FileCreateParams;
381import com.openai.models.files.FileObject;
382import com.openai.models.files.FilePurpose;
383
384FileCreateParams params = FileCreateParams.builder()
385 .purpose(FilePurpose.FINE_TUNE)
386 .file("content".getBytes())
387 .build();
388FileObject fileObject = client.files().create(params);
389```
390
391Note that when passing a non-`Path` its filename is unknown so it will not be included in the request. To manually set a filename, pass a [`MultipartField`](openai-java-core/src/main/kotlin/com/openai/core/Values.kt):
392
393```java
394import com.openai.core.MultipartField;
395import com.openai.models.files.FileCreateParams;
396import com.openai.models.files.FileObject;
397import com.openai.models.files.FilePurpose;
398import java.io.InputStream;
399import java.net.URL;
400
401FileCreateParams params = FileCreateParams.builder()
402 .purpose(FilePurpose.FINE_TUNE)
403 .file(MultipartField.<InputStream>builder()
404 .value(new URL("https://example.com/input.jsonl").openStream())
405 .filename("input.jsonl")
406 .build())
407 .build();
408FileObject fileObject = client.files().create(params);
409```
410
411## Binary responses
412
413The SDK defines methods that return binary responses, which are used for API responses that shouldn't necessarily be parsed, like non-JSON data.
414
415These methods return [`HttpResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/HttpResponse.kt):
416
417```java
418import com.openai.core.http.HttpResponse;
419import com.openai.models.files.FileContentParams;
420
421FileContentParams params = FileContentParams.builder()
422 .fileId("file_id")
423 .build();
424HttpResponse response = client.files().content(params);
425```
426
427To save the response content to a file, use the [`Files.copy(...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#copy-java.io.InputStream-java.nio.file.Path-java.nio.file.CopyOption...-) method:
428
429```java
430import com.openai.core.http.HttpResponse;
431import java.nio.file.Files;
432import java.nio.file.Paths;
433import java.nio.file.StandardCopyOption;
434
435try (HttpResponse response = client.files().content(params)) {
436 Files.copy(
437 response.body(),
438 Paths.get(path),
439 StandardCopyOption.REPLACE_EXISTING
440 );
441} catch (Exception e) {
442 System.out.println("Something went wrong!");
443 throw new RuntimeException(e);
444}
445```
446
447Or transfer the response content to any [`OutputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html):
448
449```java
450import com.openai.core.http.HttpResponse;
451import java.nio.file.Files;
452import java.nio.file.Paths;
453
454try (HttpResponse response = client.files().content(params)) {
455 response.body().transferTo(Files.newOutputStream(Paths.get(path)));
456} catch (Exception e) {
457 System.out.println("Something went wrong!");
458 throw new RuntimeException(e);
459}
460```
461
462## Raw responses
463
464The SDK defines methods that deserialize responses into instances of Java classes. However, these methods don't provide access to the response headers, status code, or the raw response body.
465
466To access this data, prefix any HTTP method call on a client or service with `withRawResponse()`:
467
468```java
469import com.openai.core.http.Headers;
470import com.openai.core.http.HttpResponseFor;
471import com.openai.models.ChatModel;
472import com.openai.models.chat.completions.ChatCompletion;
473import com.openai.models.chat.completions.ChatCompletionCreateParams;
474
475ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
476 .addUserMessage("Say this is a test")
477 .model(ChatModel.O3_MINI)
478 .build();
479HttpResponseFor<ChatCompletion> chatCompletion = client.chat().completions().withRawResponse().create(params);
480
481int statusCode = chatCompletion.statusCode();
482Headers headers = chatCompletion.headers();
483```
484
485You can still deserialize the response into an instance of a Java class if needed:
486
487```java
488import com.openai.models.chat.completions.ChatCompletion;
489
490ChatCompletion parsedChatCompletion = chatCompletion.parse();
491```
492
493### Request IDs
494
495> For more information on debugging requests, see [the API docs](https://platform.openai.com/docs/api-reference/debugging-requests).
496
497When using raw responses, you can access the `x-request-id` response header using the `requestId()` method:
498
499```java
500import com.openai.core.http.HttpResponseFor;
501import com.openai.models.chat.completions.ChatCompletion;
502import java.util.Optional;
503
504HttpResponseFor<ChatCompletion> chatCompletion = client.chat().completions().withRawResponse().create(params);
505Optional<String> requestId = chatCompletion.requestId();
506```
507
508This can be used to quickly log failing requests and report them back to OpenAI.
509
510## Error handling
511
512The SDK throws custom unchecked exception types:
513
514- [`OpenAIServiceException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code:
515
516 | Status | Exception |
517 | ------ | ---------------------------------------------------------------------------------------------------------------------- |
518 | 400 | [`BadRequestException`](openai-java-core/src/main/kotlin/com/openai/errors/BadRequestException.kt) |
519 | 401 | [`UnauthorizedException`](openai-java-core/src/main/kotlin/com/openai/errors/UnauthorizedException.kt) |
520 | 403 | [`PermissionDeniedException`](openai-java-core/src/main/kotlin/com/openai/errors/PermissionDeniedException.kt) |
521 | 404 | [`NotFoundException`](openai-java-core/src/main/kotlin/com/openai/errors/NotFoundException.kt) |
522 | 422 | [`UnprocessableEntityException`](openai-java-core/src/main/kotlin/com/openai/errors/UnprocessableEntityException.kt) |
523 | 429 | [`RateLimitException`](openai-java-core/src/main/kotlin/com/openai/errors/RateLimitException.kt) |
524 | 5xx | [`InternalServerException`](openai-java-core/src/main/kotlin/com/openai/errors/InternalServerException.kt) |
525 | others | [`UnexpectedStatusCodeException`](openai-java-core/src/main/kotlin/com/openai/errors/UnexpectedStatusCodeException.kt) |
526
527 [`SseException`](openai-java-core/src/main/kotlin/com/openai/errors/SseException.kt) is thrown for errors encountered during [SSE streaming](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) after a successful initial HTTP response.
528
529- [`OpenAIIoException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIIoException.kt): I/O networking errors.
530
531- [`OpenAIInvalidDataException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIInvalidDataException.kt): Failure to interpret successfully parsed data. For example, when accessing a property that's supposed to be required, but the API unexpectedly omitted it from the response.
532
533- [`OpenAIException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIException.kt): Base class for all exceptions. Most errors will result in one of the previously mentioned ones, but completely generic errors may be thrown using the base class.
534
535## Pagination
536
537For methods that return a paginated list of results, this library provides convenient ways access the results either one page at a time, or item-by-item across all pages.
538
539### Auto-pagination
540
541To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you:
542
543### Synchronous
544
545```java
546import com.openai.models.finetuning.jobs.FineTuningJob;
547import com.openai.models.finetuning.jobs.JobListPage;
548
549// As an Iterable:
550JobListPage page = client.fineTuning().jobs().list(params);
551for (FineTuningJob job : page.autoPager()) {
552 System.out.println(job);
553};
554
555// As a Stream:
556client.fineTuning().jobs().list(params).autoPager().stream()
557 .limit(50)
558 .forEach(job -> System.out.println(job));
559```
560
561### Asynchronous
562
563```java
564// Using forEach, which returns CompletableFuture<Void>:
565asyncClient.fineTuning().jobs().list(params).autoPager()
566 .forEach(job -> System.out.println(job), executor);
567```
568
569### Manual pagination
570
571If none of the above helpers meet your needs, you can also manually request pages one-by-one. A page of results has a `data()` method to fetch the list of objects, as well as top-level `response` and other methods to fetch top-level data about the page. It also has methods `hasNextPage`, `getNextPage`, and `getNextPageParams` methods to help with pagination.
572
573```java
574import com.openai.models.finetuning.jobs.FineTuningJob;
575import com.openai.models.finetuning.jobs.JobListPage;
576
577JobListPage page = client.fineTuning().jobs().list(params);
578while (page != null) {
579 for (FineTuningJob job : page.data()) {
580 System.out.println(job);
581 }
582
583 page = page.getNextPage().orElse(null);
584}
585```
586
587## Logging
588
589The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).
590
591Enable logging by setting the `OPENAI_LOG` environment variable to `info`:
592
593```sh
594$ export OPENAI_LOG=info
595```
596
597Or to `debug` for more verbose logging:
598
599```sh
600$ export OPENAI_LOG=debug
601```
602
603## Microsoft Azure
604
605To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same
606OpenAI client builder but with the Azure-specific configuration.
607
608```java
609OpenAIClient client = OpenAIOkHttpClient.builder()
610 // Gets the API key from the `AZURE_OPENAI_KEY` environment variable
611 .fromEnv()
612 // Set the Azure Entra ID
613 .credential(BearerTokenCredential.create(AuthenticationUtil.getBearerTokenSupplier(
614 new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default")))
615 .build();
616```
617
618See the complete Azure OpenAI example in the [`openai-java-example`](openai-java-example/src/main/java/com/openai/example/AzureEntraIdExample.java) directory. The other examples in the directory also work with Azure as long as the client is configured to use it.
619
620## Network options
621
622### Retries
623
624The SDK automatically retries 2 times by default, with a short exponential backoff.
625
626Only the following error types are retried:
627
628- Connection errors (for example, due to a network connectivity problem)
629- 408 Request Timeout
630- 409 Conflict
631- 429 Rate Limit
632- 5xx Internal
633
634The API may also explicitly instruct the SDK to retry or not retry a response.
635
636To set a custom number of retries, configure the client using the `maxRetries` method:
637
638```java
639import com.openai.client.OpenAIClient;
640import com.openai.client.okhttp.OpenAIOkHttpClient;
641
642OpenAIClient client = OpenAIOkHttpClient.builder()
643 .fromEnv()
644 .maxRetries(4)
645 .build();
646```
647
648### Timeouts
649
650Requests time out after 10 minutes by default.
651
652To set a custom timeout, configure the method call using the `timeout` method:
653
654```java
655import com.openai.models.ChatModel;
656import com.openai.models.chat.completions.ChatCompletion;
657import com.openai.models.chat.completions.ChatCompletionCreateParams;
658
659ChatCompletion chatCompletion = client.chat().completions().create(
660 params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()
661);
662```
663
664Or configure the default for all method calls at the client level:
665
666```java
667import com.openai.client.OpenAIClient;
668import com.openai.client.okhttp.OpenAIOkHttpClient;
669import java.time.Duration;
670
671OpenAIClient client = OpenAIOkHttpClient.builder()
672 .fromEnv()
673 .timeout(Duration.ofSeconds(30))
674 .build();
675```
676
677### Proxies
678
679To route requests through a proxy, configure the client using the `proxy` method:
680
681```java
682import com.openai.client.OpenAIClient;
683import com.openai.client.okhttp.OpenAIOkHttpClient;
684import java.net.InetSocketAddress;
685import java.net.Proxy;
686
687OpenAIClient client = OpenAIOkHttpClient.builder()
688 .fromEnv()
689 .proxy(new Proxy(
690 Proxy.Type.HTTP, new InetSocketAddress(
691 "https://example.com", 8080
692 )
693 ))
694 .build();
695```
696
697## Undocumented API functionality
698
699The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.
700
701### Parameters
702
703To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:
704
705```java
706import com.openai.core.JsonValue;
707import com.openai.models.chat.completions.ChatCompletionCreateParams;
708
709ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
710 .putAdditionalHeader("Secret-Header", "42")
711 .putAdditionalQueryParam("secret_query_param", "42")
712 .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
713 .build();
714```
715
716These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.
717
718To set undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class:
719
720```java
721import com.openai.core.JsonValue;
722import com.openai.models.chat.completions.ChatCompletionCreateParams;
723
724ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
725 .responseFormat(ChatCompletionCreateParams.ResponseFormat.builder()
726 .putAdditionalProperty("secretProperty", JsonValue.from("42"))
727 .build())
728 .build();
729```
730
731These properties can be accessed on the nested built object later using the `_additionalProperties()` method.
732
733To set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](openai-java-core/src/main/kotlin/com/openai/core/Values.kt) object to its setter:
734
735```java
736import com.openai.core.JsonValue;
737import com.openai.models.chat.completions.ChatCompletionCreateParams;
738
739ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
740 .addUserMessage("Say this is a test")
741 .model(JsonValue.from(42))
742 .build();
743```
744
745The most straightforward way to create a [`JsonValue`](openai-java-core/src/main/kotlin/com/openai/core/Values.kt) is using its `from(...)` method:
746
747```java
748import com.openai.core.JsonValue;
749import java.util.List;
750import java.util.Map;
751
752// Create primitive JSON values
753JsonValue nullValue = JsonValue.from(null);
754JsonValue booleanValue = JsonValue.from(true);
755JsonValue numberValue = JsonValue.from(42);
756JsonValue stringValue = JsonValue.from("Hello World!");
757
758// Create a JSON array value equivalent to `["Hello", "World"]`
759JsonValue arrayValue = JsonValue.from(List.of(
760 "Hello", "World"
761));
762
763// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
764JsonValue objectValue = JsonValue.from(Map.of(
765 "a", 1,
766 "b", 2
767));
768
769// Create an arbitrarily nested JSON equivalent to:
770// {
771// "a": [1, 2],
772// "b": [3, 4]
773// }
774JsonValue complexValue = JsonValue.from(Map.of(
775 "a", List.of(
776 1, 2
777 ),
778 "b", List.of(
779 3, 4
780 )
781));
782```
783
784### Response properties
785
786To access undocumented response properties, call the `_additionalProperties()` method:
787
788```java
789import com.openai.core.JsonValue;
790import java.util.Map;
791
792Map<String, JsonValue> additionalProperties = client.chat().completions().create(params)._additionalProperties();
793JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
794
795String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
796 @Override
797 public String visitNull() {
798 return "It's null!";
799 }
800
801 @Override
802 public String visitBoolean(boolean value) {
803 return "It's a boolean!";
804 }
805
806 @Override
807 public String visitNumber(Number value) {
808 return "It's a number!";
809 }
810
811 // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`
812 // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden
813});
814```
815
816To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method:
817
818```java
819import com.openai.core.JsonField;
820import com.openai.models.chat.completions.ChatCompletionMessageParam;
821import java.util.Optional;
822
823JsonField<List<ChatCompletionMessageParam>> messages = client.chat().completions().create(params)._messages();
824
825if (messages.isMissing()) {
826 // The property is absent from the JSON response
827} else if (messages.isNull()) {
828 // The property was set to literal null
829} else {
830 // Check if value was provided as a string
831 // Other methods include `asNumber()`, `asBoolean()`, etc.
832 Optional<String> jsonString = messages.asString();
833
834 // Try to deserialize into a custom type
835 MyClass myObject = messages.asUnknown().orElseThrow().convert(MyClass.class);
836}
837```
838
839### Response validation
840
841In rare cases, the API may return a response that doesn't match the expected type. For example, the SDK may expect a property to contain a `String`, but the API could return something else.
842
843By default, the SDK will not throw an exception in this case. It will throw [`OpenAIInvalidDataException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIInvalidDataException.kt) only if you directly access the property.
844
845If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:
846
847```java
848import com.openai.models.chat.completions.ChatCompletion;
849
850ChatCompletion chatCompletion = client.chat().completions().create(params).validate();
851```
852
853Or configure the method call to validate the response using the `responseValidation` method:
854
855```java
856import com.openai.models.ChatModel;
857import com.openai.models.chat.completions.ChatCompletion;
858import com.openai.models.chat.completions.ChatCompletionCreateParams;
859
860ChatCompletion chatCompletion = client.chat().completions().create(
861 params, RequestOptions.builder().responseValidation(true).build()
862);
863```
864
865Or configure the default for all method calls at the client level:
866
867```java
868import com.openai.client.OpenAIClient;
869import com.openai.client.okhttp.OpenAIOkHttpClient;
870
871OpenAIClient client = OpenAIOkHttpClient.builder()
872 .fromEnv()
873 .responseValidation(true)
874 .build();
875```
876
877## FAQ
878
879### Why don't you use plain `enum` classes?
880
881Java `enum` classes are not trivially [forwards compatible](https://www.stainless.com/blog/making-java-enums-forwards-compatible). Using them in the SDK could cause runtime exceptions if the API is updated to respond with a new enum value.
882
883### Why do you represent fields using `JsonField<T>` instead of just plain `T`?
884
885Using `JsonField<T>` enables a few features:
886
887- Allowing usage of [undocumented API functionality](#undocumented-api-functionality)
888- Lazily [validating the API response against the expected shape](#response-validation)
889- Representing absent vs explicitly null values
890
891### Why don't you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)?
892
893It is not [backwards compatible to add new fields to a data class](https://kotlinlang.org/docs/api-guidelines-backward-compatibility.html#avoid-using-data-classes-in-your-api) and we don't want to introduce a breaking change every time we add a field to a class.
894
895### Why don't you use checked exceptions?
896
897Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.
898
899Checked exceptions:
900
901- Are verbose to handle
902- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error
903- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function)
904- Don't play well with lambdas (also due to the function coloring problem)
905
906## Semantic versioning
907
908This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
909
9101. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
9112. Changes that we do not expect to impact the vast majority of users in practice.
912
913We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
914
915We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-java/issues) with questions, bugs, or suggestions.