openai/openai-java

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.37.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

846lines · 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.37.0)
13[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.37.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.37.0)
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.37.0).
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.37.0")
33```
34
35### Maven
36
37```xml
38<dependency>
39 <groupId>com.openai</groupId>
40 <artifactId>openai-java</artifactId>
41 <version>0.37.0</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## File uploads
292
293The SDK defines methods that accept files.
294
295To upload a file, pass a [`Path`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html):
296
297```java
298import com.openai.models.files.FileCreateParams;
299import com.openai.models.files.FileObject;
300import com.openai.models.files.FilePurpose;
301import java.nio.file.Paths;
302
303FileCreateParams params = FileCreateParams.builder()
304 .purpose(FilePurpose.FINE_TUNE)
305 .file(Paths.get("input.jsonl"))
306 .build();
307FileObject fileObject = client.files().create(params);
308```
309
310Or an arbitrary [`InputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html):
311
312```java
313import com.openai.models.files.FileCreateParams;
314import com.openai.models.files.FileObject;
315import com.openai.models.files.FilePurpose;
316import java.net.URL;
317
318FileCreateParams params = FileCreateParams.builder()
319 .purpose(FilePurpose.FINE_TUNE)
320 .file(new URL("https://example.com/input.jsonl").openStream())
321 .build();
322FileObject fileObject = client.files().create(params);
323```
324
325Or a `byte[]` array:
326
327```java
328import com.openai.models.files.FileCreateParams;
329import com.openai.models.files.FileObject;
330import com.openai.models.files.FilePurpose;
331
332FileCreateParams params = FileCreateParams.builder()
333 .purpose(FilePurpose.FINE_TUNE)
334 .file("content".getBytes())
335 .build();
336FileObject fileObject = client.files().create(params);
337```
338
339Note 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):
340
341```java
342import com.openai.core.MultipartField;
343import com.openai.models.files.FileCreateParams;
344import com.openai.models.files.FileObject;
345import com.openai.models.files.FilePurpose;
346import java.io.InputStream;
347import java.net.URL;
348
349FileCreateParams params = FileCreateParams.builder()
350 .purpose(FilePurpose.FINE_TUNE)
351 .file(MultipartField.<InputStream>builder()
352 .value(new URL("https://example.com/input.jsonl").openStream())
353 .filename("input.jsonl")
354 .build())
355 .build();
356FileObject fileObject = client.files().create(params);
357```
358
359## Binary responses
360
361The SDK defines methods that return binary responses, which are used for API responses that shouldn't necessarily be parsed, like non-JSON data.
362
363These methods return [`HttpResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/HttpResponse.kt):
364
365```java
366import com.openai.core.http.HttpResponse;
367import com.openai.models.files.FileContentParams;
368
369FileContentParams params = FileContentParams.builder()
370 .fileId("file_id")
371 .build();
372HttpResponse response = client.files().content(params);
373```
374
375To 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:
376
377```java
378import com.openai.core.http.HttpResponse;
379import java.nio.file.Files;
380import java.nio.file.Paths;
381import java.nio.file.StandardCopyOption;
382
383try (HttpResponse response = client.files().content(params)) {
384 Files.copy(
385 response.body(),
386 Paths.get(path),
387 StandardCopyOption.REPLACE_EXISTING
388 );
389} catch (Exception e) {
390 System.out.println("Something went wrong!");
391 throw new RuntimeException(e);
392}
393```
394
395Or transfer the response content to any [`OutputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html):
396
397```java
398import com.openai.core.http.HttpResponse;
399import java.nio.file.Files;
400import java.nio.file.Paths;
401
402try (HttpResponse response = client.files().content(params)) {
403 response.body().transferTo(Files.newOutputStream(Paths.get(path)));
404} catch (Exception e) {
405 System.out.println("Something went wrong!");
406 throw new RuntimeException(e);
407}
408```
409
410## Raw responses
411
412The 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.
413
414To access this data, prefix any HTTP method call on a client or service with `withRawResponse()`:
415
416```java
417import com.openai.core.http.Headers;
418import com.openai.core.http.HttpResponseFor;
419import com.openai.models.ChatModel;
420import com.openai.models.chat.completions.ChatCompletion;
421import com.openai.models.chat.completions.ChatCompletionCreateParams;
422
423ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
424 .addUserMessage("Say this is a test")
425 .model(ChatModel.O3_MINI)
426 .build();
427HttpResponseFor<ChatCompletion> chatCompletion = client.chat().completions().withRawResponse().create(params);
428
429int statusCode = chatCompletion.statusCode();
430Headers headers = chatCompletion.headers();
431```
432
433You can still deserialize the response into an instance of a Java class if needed:
434
435```java
436import com.openai.models.chat.completions.ChatCompletion;
437
438ChatCompletion parsedChatCompletion = chatCompletion.parse();
439```
440
441## Error handling
442
443The SDK throws custom unchecked exception types:
444
445- [`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:
446
447 | Status | Exception |
448 | ------ | ---------------------------------------------------------------------------------------------------------------------- |
449 | 400 | [`BadRequestException`](openai-java-core/src/main/kotlin/com/openai/errors/BadRequestException.kt) |
450 | 401 | [`UnauthorizedException`](openai-java-core/src/main/kotlin/com/openai/errors/UnauthorizedException.kt) |
451 | 403 | [`PermissionDeniedException`](openai-java-core/src/main/kotlin/com/openai/errors/PermissionDeniedException.kt) |
452 | 404 | [`NotFoundException`](openai-java-core/src/main/kotlin/com/openai/errors/NotFoundException.kt) |
453 | 422 | [`UnprocessableEntityException`](openai-java-core/src/main/kotlin/com/openai/errors/UnprocessableEntityException.kt) |
454 | 429 | [`RateLimitException`](openai-java-core/src/main/kotlin/com/openai/errors/RateLimitException.kt) |
455 | 5xx | [`InternalServerException`](openai-java-core/src/main/kotlin/com/openai/errors/InternalServerException.kt) |
456 | others | [`UnexpectedStatusCodeException`](openai-java-core/src/main/kotlin/com/openai/errors/UnexpectedStatusCodeException.kt) |
457
458 [`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.
459
460- [`OpenAIIoException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIIoException.kt): I/O networking errors.
461
462- [`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.
463
464- [`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.
465
466## Pagination
467
468For 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.
469
470### Auto-pagination
471
472To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you:
473
474### Synchronous
475
476```java
477import com.openai.models.finetuning.jobs.FineTuningJob;
478import com.openai.models.finetuning.jobs.JobListPage;
479
480// As an Iterable:
481JobListPage page = client.fineTuning().jobs().list(params);
482for (FineTuningJob job : page.autoPager()) {
483 System.out.println(job);
484};
485
486// As a Stream:
487client.fineTuning().jobs().list(params).autoPager().stream()
488 .limit(50)
489 .forEach(job -> System.out.println(job));
490```
491
492### Asynchronous
493
494```java
495// Using forEach, which returns CompletableFuture<Void>:
496asyncClient.fineTuning().jobs().list(params).autoPager()
497 .forEach(job -> System.out.println(job), executor);
498```
499
500### Manual pagination
501
502If 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.
503
504```java
505import com.openai.models.finetuning.jobs.FineTuningJob;
506import com.openai.models.finetuning.jobs.JobListPage;
507
508JobListPage page = client.fineTuning().jobs().list(params);
509while (page != null) {
510 for (FineTuningJob job : page.data()) {
511 System.out.println(job);
512 }
513
514 page = page.getNextPage().orElse(null);
515}
516```
517
518## Logging
519
520The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).
521
522Enable logging by setting the `OPENAI_LOG` environment variable to `info`:
523
524```sh
525$ export OPENAI_LOG=info
526```
527
528Or to `debug` for more verbose logging:
529
530```sh
531$ export OPENAI_LOG=debug
532```
533
534## Microsoft Azure
535
536To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same
537OpenAI client builder but with the Azure-specific configuration.
538
539```java
540OpenAIClient client = OpenAIOkHttpClient.builder()
541 // Gets the API key from the `AZURE_OPENAI_KEY` environment variable
542 .fromEnv()
543 // Set the Azure Entra ID
544 .credential(BearerTokenCredential.create(AuthenticationUtil.getBearerTokenSupplier(
545 new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default")))
546 .build();
547```
548
549See 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.
550
551## Network options
552
553### Retries
554
555The SDK automatically retries 2 times by default, with a short exponential backoff.
556
557Only the following error types are retried:
558
559- Connection errors (for example, due to a network connectivity problem)
560- 408 Request Timeout
561- 409 Conflict
562- 429 Rate Limit
563- 5xx Internal
564
565The API may also explicitly instruct the SDK to retry or not retry a response.
566
567To set a custom number of retries, configure the client using the `maxRetries` method:
568
569```java
570import com.openai.client.OpenAIClient;
571import com.openai.client.okhttp.OpenAIOkHttpClient;
572
573OpenAIClient client = OpenAIOkHttpClient.builder()
574 .fromEnv()
575 .maxRetries(4)
576 .build();
577```
578
579### Timeouts
580
581Requests time out after 10 minutes by default.
582
583To set a custom timeout, configure the method call using the `timeout` method:
584
585```java
586import com.openai.models.ChatModel;
587import com.openai.models.chat.completions.ChatCompletion;
588import com.openai.models.chat.completions.ChatCompletionCreateParams;
589
590ChatCompletion chatCompletion = client.chat().completions().create(
591 params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()
592);
593```
594
595Or configure the default for all method calls at the client level:
596
597```java
598import com.openai.client.OpenAIClient;
599import com.openai.client.okhttp.OpenAIOkHttpClient;
600import java.time.Duration;
601
602OpenAIClient client = OpenAIOkHttpClient.builder()
603 .fromEnv()
604 .timeout(Duration.ofSeconds(30))
605 .build();
606```
607
608### Proxies
609
610To route requests through a proxy, configure the client using the `proxy` method:
611
612```java
613import com.openai.client.OpenAIClient;
614import com.openai.client.okhttp.OpenAIOkHttpClient;
615import java.net.InetSocketAddress;
616import java.net.Proxy;
617
618OpenAIClient client = OpenAIOkHttpClient.builder()
619 .fromEnv()
620 .proxy(new Proxy(
621 Proxy.Type.HTTP, new InetSocketAddress(
622 "https://example.com", 8080
623 )
624 ))
625 .build();
626```
627
628## Undocumented API functionality
629
630The 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.
631
632### Parameters
633
634To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:
635
636```java
637import com.openai.core.JsonValue;
638import com.openai.models.chat.completions.ChatCompletionCreateParams;
639
640ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
641 .putAdditionalHeader("Secret-Header", "42")
642 .putAdditionalQueryParam("secret_query_param", "42")
643 .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
644 .build();
645```
646
647These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.
648
649To set undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class:
650
651```java
652import com.openai.core.JsonValue;
653import com.openai.models.chat.completions.ChatCompletionCreateParams;
654
655ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
656 .responseFormat(ChatCompletionCreateParams.ResponseFormat.builder()
657 .putAdditionalProperty("secretProperty", JsonValue.from("42"))
658 .build())
659 .build();
660```
661
662These properties can be accessed on the nested built object later using the `_additionalProperties()` method.
663
664To 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:
665
666```java
667import com.openai.core.JsonValue;
668import com.openai.models.chat.completions.ChatCompletionCreateParams;
669
670ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
671 .addUserMessage("Say this is a test")
672 .model(JsonValue.from(42))
673 .build();
674```
675
676The most straightforward way to create a [`JsonValue`](openai-java-core/src/main/kotlin/com/openai/core/Values.kt) is using its `from(...)` method:
677
678```java
679import com.openai.core.JsonValue;
680import java.util.List;
681import java.util.Map;
682
683// Create primitive JSON values
684JsonValue nullValue = JsonValue.from(null);
685JsonValue booleanValue = JsonValue.from(true);
686JsonValue numberValue = JsonValue.from(42);
687JsonValue stringValue = JsonValue.from("Hello World!");
688
689// Create a JSON array value equivalent to `["Hello", "World"]`
690JsonValue arrayValue = JsonValue.from(List.of(
691 "Hello", "World"
692));
693
694// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
695JsonValue objectValue = JsonValue.from(Map.of(
696 "a", 1,
697 "b", 2
698));
699
700// Create an arbitrarily nested JSON equivalent to:
701// {
702// "a": [1, 2],
703// "b": [3, 4]
704// }
705JsonValue complexValue = JsonValue.from(Map.of(
706 "a", List.of(
707 1, 2
708 ),
709 "b", List.of(
710 3, 4
711 )
712));
713```
714
715### Response properties
716
717To access undocumented response properties, call the `_additionalProperties()` method:
718
719```java
720import com.openai.core.JsonValue;
721import java.util.Map;
722
723Map<String, JsonValue> additionalProperties = client.chat().completions().create(params)._additionalProperties();
724JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
725
726String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
727 @Override
728 public String visitNull() {
729 return "It's null!";
730 }
731
732 @Override
733 public String visitBoolean(boolean value) {
734 return "It's a boolean!";
735 }
736
737 @Override
738 public String visitNumber(Number value) {
739 return "It's a number!";
740 }
741
742 // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`
743 // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden
744});
745```
746
747To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method:
748
749```java
750import com.openai.core.JsonField;
751import com.openai.models.chat.completions.ChatCompletionMessageParam;
752import java.util.Optional;
753
754JsonField<List<ChatCompletionMessageParam>> messages = client.chat().completions().create(params)._messages();
755
756if (messages.isMissing()) {
757 // The property is absent from the JSON response
758} else if (messages.isNull()) {
759 // The property was set to literal null
760} else {
761 // Check if value was provided as a string
762 // Other methods include `asNumber()`, `asBoolean()`, etc.
763 Optional<String> jsonString = messages.asString();
764
765 // Try to deserialize into a custom type
766 MyClass myObject = messages.asUnknown().orElseThrow().convert(MyClass.class);
767}
768```
769
770### Response validation
771
772In 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.
773
774By 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.
775
776If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:
777
778```java
779import com.openai.models.chat.completions.ChatCompletion;
780
781ChatCompletion chatCompletion = client.chat().completions().create(params).validate();
782```
783
784Or configure the method call to validate the response using the `responseValidation` method:
785
786```java
787import com.openai.models.ChatModel;
788import com.openai.models.chat.completions.ChatCompletion;
789import com.openai.models.chat.completions.ChatCompletionCreateParams;
790
791ChatCompletion chatCompletion = client.chat().completions().create(
792 params, RequestOptions.builder().responseValidation(true).build()
793);
794```
795
796Or configure the default for all method calls at the client level:
797
798```java
799import com.openai.client.OpenAIClient;
800import com.openai.client.okhttp.OpenAIOkHttpClient;
801
802OpenAIClient client = OpenAIOkHttpClient.builder()
803 .fromEnv()
804 .responseValidation(true)
805 .build();
806```
807
808## FAQ
809
810### Why don't you use plain `enum` classes?
811
812Java `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.
813
814### Why do you represent fields using `JsonField<T>` instead of just plain `T`?
815
816Using `JsonField<T>` enables a few features:
817
818- Allowing usage of [undocumented API functionality](#undocumented-api-functionality)
819- Lazily [validating the API response against the expected shape](#response-validation)
820- Representing absent vs explicitly null values
821
822### Why don't you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)?
823
824It 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.
825
826### Why don't you use checked exceptions?
827
828Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.
829
830Checked exceptions:
831
832- Are verbose to handle
833- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error
834- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function)
835- Don't play well with lambdas (also due to the function coloring problem)
836
837## Semantic versioning
838
839This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
840
8411. 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.)_
8422. Changes that we do not expect to impact the vast majority of users in practice.
843
844We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
845
846We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-java/issues) with questions, bugs, or suggestions.