openai/openai-java

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.4.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

971lines · 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.4.0)
6[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/1.4.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/1.4.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 available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/1.4.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.4.0")
26```
27
28### Maven
29
30```xml
31<dependency>
32 <groupId>com.openai</groupId>
33 <artifactId>openai-java</artifactId>
34 <version>1.4.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_4_1)
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.GPT_4_1)
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.GPT_4_1)
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.GPT_4_1)
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.GPT_4_1)
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## Jackson
598
599The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.
600
601The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config).
602
603If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) or [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt).
604
605> [!CAUTION]
606> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.
607
608## Microsoft Azure
609
610To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same
611OpenAI client builder but with the Azure-specific configuration.
612
613```java
614OpenAIClient client = OpenAIOkHttpClient.builder()
615 // Gets the API key and endpoint from the `AZURE_OPENAI_KEY` and `OPENAI_BASE_URL` environment variables, respectively
616 .fromEnv()
617 // Set the Azure Entra ID
618 .credential(BearerTokenCredential.create(AuthenticationUtil.getBearerTokenSupplier(
619 new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default")))
620 .build();
621```
622
623See 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.
624
625## Network options
626
627### Retries
628
629The SDK automatically retries 2 times by default, with a short exponential backoff.
630
631Only the following error types are retried:
632
633- Connection errors (for example, due to a network connectivity problem)
634- 408 Request Timeout
635- 409 Conflict
636- 429 Rate Limit
637- 5xx Internal
638
639The API may also explicitly instruct the SDK to retry or not retry a response.
640
641To set a custom number of retries, configure the client using the `maxRetries` method:
642
643```java
644import com.openai.client.OpenAIClient;
645import com.openai.client.okhttp.OpenAIOkHttpClient;
646
647OpenAIClient client = OpenAIOkHttpClient.builder()
648 .fromEnv()
649 .maxRetries(4)
650 .build();
651```
652
653### Timeouts
654
655Requests time out after 10 minutes by default.
656
657To set a custom timeout, configure the method call using the `timeout` method:
658
659```java
660import com.openai.models.ChatModel;
661import com.openai.models.chat.completions.ChatCompletion;
662import com.openai.models.chat.completions.ChatCompletionCreateParams;
663
664ChatCompletion chatCompletion = client.chat().completions().create(
665 params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()
666);
667```
668
669Or configure the default for all method calls at the client level:
670
671```java
672import com.openai.client.OpenAIClient;
673import com.openai.client.okhttp.OpenAIOkHttpClient;
674import java.time.Duration;
675
676OpenAIClient client = OpenAIOkHttpClient.builder()
677 .fromEnv()
678 .timeout(Duration.ofSeconds(30))
679 .build();
680```
681
682### Proxies
683
684To route requests through a proxy, configure the client using the `proxy` method:
685
686```java
687import com.openai.client.OpenAIClient;
688import com.openai.client.okhttp.OpenAIOkHttpClient;
689import java.net.InetSocketAddress;
690import java.net.Proxy;
691
692OpenAIClient client = OpenAIOkHttpClient.builder()
693 .fromEnv()
694 .proxy(new Proxy(
695 Proxy.Type.HTTP, new InetSocketAddress(
696 "https://example.com", 8080
697 )
698 ))
699 .build();
700```
701
702### Custom HTTP client
703
704The SDK consists of three artifacts:
705
706- `openai-java-core`
707 - Contains core SDK logic
708 - Does not depend on [OkHttp](https://square.github.io/okhttp)
709 - Exposes [`OpenAIClient`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt), [`OpenAIClientAsync`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsync.kt), [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt), and [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), all of which can work with any HTTP client
710- `openai-java-client-okhttp`
711 - Depends on [OkHttp](https://square.github.io/okhttp)
712 - Exposes [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) and [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt), which provide a way to construct [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt) and [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), respectively, using OkHttp
713- `openai-java`
714 - Depends on and exposes the APIs of both `openai-java-core` and `openai-java-client-okhttp`
715 - Does not have its own logic
716
717This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies.
718
719#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html)
720
721> [!TIP]
722> Try the available [network options](#network-options) before replacing the default client.
723
724To use a customized `OkHttpClient`:
725
7261. Replace your [`openai-java` dependency](#installation) with `openai-java-core`
7272. Copy `openai-java-client-okhttp`'s [`OkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt) class into your code and customize it
7283. Construct [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt) or [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), similarly to [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) or [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt), using your customized client
729
730### Completely custom HTTP client
731
732To use a completely custom HTTP client:
733
7341. Replace your [`openai-java` dependency](#installation) with `openai-java-core`
7352. Write a class that implements the [`HttpClient`](openai-java-core/src/main/kotlin/com/openai/core/http/HttpClient.kt) interface
7363. Construct [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt) or [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), similarly to [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) or [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt), using your new client class
737
738## Undocumented API functionality
739
740The 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.
741
742### Parameters
743
744To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:
745
746```java
747import com.openai.core.JsonValue;
748import com.openai.models.chat.completions.ChatCompletionCreateParams;
749
750ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
751 .putAdditionalHeader("Secret-Header", "42")
752 .putAdditionalQueryParam("secret_query_param", "42")
753 .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
754 .build();
755```
756
757These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.
758
759To set undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class:
760
761```java
762import com.openai.core.JsonValue;
763import com.openai.models.chat.completions.ChatCompletionCreateParams;
764
765ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
766 .responseFormat(ChatCompletionCreateParams.ResponseFormat.builder()
767 .putAdditionalProperty("secretProperty", JsonValue.from("42"))
768 .build())
769 .build();
770```
771
772These properties can be accessed on the nested built object later using the `_additionalProperties()` method.
773
774To 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:
775
776```java
777import com.openai.core.JsonValue;
778import com.openai.models.chat.completions.ChatCompletionCreateParams;
779
780ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
781 .addUserMessage("Say this is a test")
782 .model(JsonValue.from(42))
783 .build();
784```
785
786The most straightforward way to create a [`JsonValue`](openai-java-core/src/main/kotlin/com/openai/core/Values.kt) is using its `from(...)` method:
787
788```java
789import com.openai.core.JsonValue;
790import java.util.List;
791import java.util.Map;
792
793// Create primitive JSON values
794JsonValue nullValue = JsonValue.from(null);
795JsonValue booleanValue = JsonValue.from(true);
796JsonValue numberValue = JsonValue.from(42);
797JsonValue stringValue = JsonValue.from("Hello World!");
798
799// Create a JSON array value equivalent to `["Hello", "World"]`
800JsonValue arrayValue = JsonValue.from(List.of(
801 "Hello", "World"
802));
803
804// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
805JsonValue objectValue = JsonValue.from(Map.of(
806 "a", 1,
807 "b", 2
808));
809
810// Create an arbitrarily nested JSON equivalent to:
811// {
812// "a": [1, 2],
813// "b": [3, 4]
814// }
815JsonValue complexValue = JsonValue.from(Map.of(
816 "a", List.of(
817 1, 2
818 ),
819 "b", List.of(
820 3, 4
821 )
822));
823```
824
825Normally 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.
826
827To forcibly omit a required parameter or property, pass [`JsonMissing`](openai-java-core/src/main/kotlin/com/openai/core/Values.kt):
828
829```java
830import com.openai.core.JsonMissing;
831import com.openai.models.ChatModel;
832import com.openai.models.chat.completions.ChatCompletionCreateParams;
833
834ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
835 .model(ChatModel.GPT_4_1)
836 .messages(JsonMissing.of())
837 .build();
838```
839
840### Response properties
841
842To access undocumented response properties, call the `_additionalProperties()` method:
843
844```java
845import com.openai.core.JsonValue;
846import java.util.Map;
847
848Map<String, JsonValue> additionalProperties = client.chat().completions().create(params)._additionalProperties();
849JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
850
851String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
852 @Override
853 public String visitNull() {
854 return "It's null!";
855 }
856
857 @Override
858 public String visitBoolean(boolean value) {
859 return "It's a boolean!";
860 }
861
862 @Override
863 public String visitNumber(Number value) {
864 return "It's a number!";
865 }
866
867 // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`
868 // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden
869});
870```
871
872To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method:
873
874```java
875import com.openai.core.JsonField;
876import com.openai.models.chat.completions.ChatCompletionMessageParam;
877import java.util.Optional;
878
879JsonField<List<ChatCompletionMessageParam>> messages = client.chat().completions().create(params)._messages();
880
881if (messages.isMissing()) {
882 // The property is absent from the JSON response
883} else if (messages.isNull()) {
884 // The property was set to literal null
885} else {
886 // Check if value was provided as a string
887 // Other methods include `asNumber()`, `asBoolean()`, etc.
888 Optional<String> jsonString = messages.asString();
889
890 // Try to deserialize into a custom type
891 MyClass myObject = messages.asUnknown().orElseThrow().convert(MyClass.class);
892}
893```
894
895### Response validation
896
897In 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.
898
899By 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.
900
901If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:
902
903```java
904import com.openai.models.chat.completions.ChatCompletion;
905
906ChatCompletion chatCompletion = client.chat().completions().create(params).validate();
907```
908
909Or configure the method call to validate the response using the `responseValidation` method:
910
911```java
912import com.openai.models.ChatModel;
913import com.openai.models.chat.completions.ChatCompletion;
914import com.openai.models.chat.completions.ChatCompletionCreateParams;
915
916ChatCompletion chatCompletion = client.chat().completions().create(
917 params, RequestOptions.builder().responseValidation(true).build()
918);
919```
920
921Or configure the default for all method calls at the client level:
922
923```java
924import com.openai.client.OpenAIClient;
925import com.openai.client.okhttp.OpenAIOkHttpClient;
926
927OpenAIClient client = OpenAIOkHttpClient.builder()
928 .fromEnv()
929 .responseValidation(true)
930 .build();
931```
932
933## FAQ
934
935### Why don't you use plain `enum` classes?
936
937Java `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.
938
939### Why do you represent fields using `JsonField<T>` instead of just plain `T`?
940
941Using `JsonField<T>` enables a few features:
942
943- Allowing usage of [undocumented API functionality](#undocumented-api-functionality)
944- Lazily [validating the API response against the expected shape](#response-validation)
945- Representing absent vs explicitly null values
946
947### Why don't you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)?
948
949It 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.
950
951### Why don't you use checked exceptions?
952
953Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.
954
955Checked exceptions:
956
957- Are verbose to handle
958- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error
959- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function)
960- Don't play well with lambdas (also due to the function coloring problem)
961
962## Semantic versioning
963
964This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
965
9661. 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.)_
9672. Changes that we do not expect to impact the vast majority of users in practice.
968
969We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
970
971We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-java/issues) with questions, bugs, or suggestions.
972