openai/openai-java

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
b8fe68a5cf8a18640d92ab9a95b87a12e5913474

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

924lines · modecode

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