openai/openai-java

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.31.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

697lines · 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.31.0)
13[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.31.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.31.0)
14
15<!-- x-release-please-end -->
16
17The OpenAI Java SDK provides convenient access to the OpenAI REST API from applications written in Java.
18
19The 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.0.1).
20
21## Installation
22
23<!-- x-release-please-start-version -->
24
25### Gradle
26
27```kotlin
28implementation("com.openai:openai-java:0.31.0")
29```
30
31### Maven
32
33```xml
34<dependency>
35 <groupId>com.openai</groupId>
36 <artifactId>openai-java</artifactId>
37 <version>0.31.0</version>
38</dependency>
39```
40
41<!-- x-release-please-end -->
42
43## Requirements
44
45This library requires Java 8 or later.
46
47## Usage
48
49See the [`openai-java-example`](openai-java-example/src/main/java/com/openai/example) directory for complete and runnable examples.
50
51```java
52import com.openai.client.OpenAIClient;
53import com.openai.client.okhttp.OpenAIOkHttpClient;
54import com.openai.models.ChatCompletion;
55import com.openai.models.ChatCompletionCreateParams;
56import com.openai.models.ChatModel;
57
58// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
59OpenAIClient client = OpenAIOkHttpClient.fromEnv();
60
61ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
62 .addUserMessage("Say this is a test")
63 .model(ChatModel.O3_MINI)
64 .build();
65ChatCompletion chatCompletion = client.chat().completions().create(params);
66```
67
68## Client configuration
69
70Configure the client using environment variables:
71
72```java
73import com.openai.client.OpenAIClient;
74import com.openai.client.okhttp.OpenAIOkHttpClient;
75
76// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
77OpenAIClient client = OpenAIOkHttpClient.fromEnv();
78```
79
80Or manually:
81
82```java
83import com.openai.client.OpenAIClient;
84import com.openai.client.okhttp.OpenAIOkHttpClient;
85
86OpenAIClient client = OpenAIOkHttpClient.builder()
87 .apiKey("My API Key")
88 .build();
89```
90
91Or using a combination of the two approaches:
92
93```java
94import com.openai.client.OpenAIClient;
95import com.openai.client.okhttp.OpenAIOkHttpClient;
96
97OpenAIClient client = OpenAIOkHttpClient.builder()
98 // Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
99 .fromEnv()
100 .apiKey("My API Key")
101 .build();
102```
103
104See this table for the available options:
105
106| Setter | Environment variable | Required | Default value |
107| -------------- | -------------------- | -------- | ------------- |
108| `apiKey` | `OPENAI_API_KEY` | true | - |
109| `organization` | `OPENAI_ORG_ID` | false | - |
110| `project` | `OPENAI_PROJECT_ID` | false | - |
111
112> [!TIP]
113> Don't create more than one client in the same application. Each client has a connection pool and
114> thread pools, which are more efficient to share between requests.
115
116## Requests and responses
117
118To 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.
119
120For example, `client.chat().completions().create(...)` should be called with an instance of `ChatCompletionCreateParams`, and it will return an instance of `ChatCompletion`.
121
122## Immutability
123
124Each 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.
125
126Each 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.
127
128Because each class is immutable, builder modification will _never_ affect already built class instances.
129
130## Asynchronous execution
131
132The default client is synchronous. To switch to asynchronous execution, call the `async()` method:
133
134```java
135import com.openai.client.OpenAIClient;
136import com.openai.client.okhttp.OpenAIOkHttpClient;
137import com.openai.models.ChatCompletion;
138import com.openai.models.ChatCompletionCreateParams;
139import com.openai.models.ChatModel;
140import java.util.concurrent.CompletableFuture;
141
142// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
143OpenAIClient client = OpenAIOkHttpClient.fromEnv();
144
145ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
146 .addUserMessage("Say this is a test")
147 .model(ChatModel.O3_MINI)
148 .build();
149CompletableFuture<ChatCompletion> chatCompletion = client.async().chat().completions().create(params);
150```
151
152Or create an asynchronous client from the beginning:
153
154```java
155import com.openai.client.OpenAIClientAsync;
156import com.openai.client.okhttp.OpenAIOkHttpClientAsync;
157import com.openai.models.ChatCompletion;
158import com.openai.models.ChatCompletionCreateParams;
159import com.openai.models.ChatModel;
160import java.util.concurrent.CompletableFuture;
161
162// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
163OpenAIClientAsync client = OpenAIOkHttpClientAsync.fromEnv();
164
165ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
166 .addUserMessage("Say this is a test")
167 .model(ChatModel.O3_MINI)
168 .build();
169CompletableFuture<ChatCompletion> chatCompletion = client.chat().completions().create(params);
170```
171
172The asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s.
173
174## Streaming
175
176The 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.
177
178Some 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.
179
180These streaming methods return [`StreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt) for synchronous clients:
181
182```java
183import com.openai.core.http.StreamResponse;
184import com.openai.models.ChatCompletionChunk;
185
186try (StreamResponse<ChatCompletionChunk> streamResponse = client.chat().completions().createStreaming(params)) {
187 streamResponse.stream().forEach(chunk -> {
188 System.out.println(chunk);
189 });
190 System.out.println("No more chunks!");
191}
192```
193
194Or [`AsyncStreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/AsyncStreamResponse.kt) for asynchronous clients:
195
196```java
197import com.openai.core.http.AsyncStreamResponse;
198import com.openai.models.ChatCompletionChunk;
199import java.util.Optional;
200
201client.async().chat().completions().createStreaming(params).subscribe(chunk -> {
202 System.out.println(chunk);
203});
204
205// If you need to handle errors or completion of the stream
206client.async().chat().completions().createStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() {
207 @Override
208 public void onNext(ChatCompletionChunk chunk) {
209 System.out.println(chunk);
210 }
211
212 @Override
213 public void onComplete(Optional<Throwable> error) {
214 if (error.isPresent()) {
215 System.out.println("Something went wrong!");
216 throw new RuntimeException(error.get());
217 } else {
218 System.out.println("No more chunks!");
219 }
220 }
221});
222
223// Or use futures
224client.async().chat().completions().createStreaming(params)
225 .subscribe(chunk -> {
226 System.out.println(chunk);
227 })
228 .onCompleteFuture();
229 .whenComplete((unused, error) -> {
230 if (error != null) {
231 System.out.println("Something went wrong!");
232 throw new RuntimeException(error);
233 } else {
234 System.out.println("No more chunks!");
235 }
236 });
237```
238
239Async 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.
240
241To use a different `Executor`, configure the subscription using the `executor` parameter:
242
243```java
244import java.util.concurrent.Executor;
245import java.util.concurrent.Executors;
246
247Executor executor = Executors.newFixedThreadPool(4);
248client.async().chat().completions().createStreaming(params).subscribe(
249 chunk -> System.out.println(chunk), executor
250);
251```
252
253Or configure the client globally using the `streamHandlerExecutor` method:
254
255```java
256import com.openai.client.OpenAIClient;
257import com.openai.client.okhttp.OpenAIOkHttpClient;
258import java.util.concurrent.Executors;
259
260OpenAIClient client = OpenAIOkHttpClient.builder()
261 .fromEnv()
262 .streamHandlerExecutor(Executors.newFixedThreadPool(4))
263 .build();
264```
265
266## Binary responses
267
268The SDK defines methods that return binary responses, which are used for API responses that shouldn't necessarily be parsed, like non-JSON data.
269
270These methods return [`HttpResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/HttpResponse.kt):
271
272```java
273import com.openai.core.http.HttpResponse;
274import com.openai.models.FileContentParams;
275
276FileContentParams params = FileContentParams.builder()
277 .fileId("file_id")
278 .build();
279HttpResponse response = client.files().content(params);
280```
281
282To 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:
283
284```java
285import com.openai.core.http.HttpResponse;
286import java.nio.file.Files;
287import java.nio.file.Paths;
288import java.nio.file.StandardCopyOption;
289
290try (HttpResponse response = client.files().content(params)) {
291 Files.copy(
292 response.body(),
293 Paths.get(path),
294 StandardCopyOption.REPLACE_EXISTING
295 );
296} catch (Exception e) {
297 System.out.println("Something went wrong!");
298 throw new RuntimeException(e);
299}
300```
301
302Or transfer the response content to any [`OutputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html):
303
304```java
305import com.openai.core.http.HttpResponse;
306import java.nio.file.Files;
307import java.nio.file.Paths;
308
309try (HttpResponse response = client.files().content(params)) {
310 response.body().transferTo(Files.newOutputStream(Paths.get(path)));
311} catch (Exception e) {
312 System.out.println("Something went wrong!");
313 throw new RuntimeException(e);
314}
315```
316
317## Raw responses
318
319The 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.
320
321To access this data, prefix any HTTP method call on a client or service with `withRawResponse()`:
322
323```java
324import com.openai.core.http.Headers;
325import com.openai.core.http.HttpResponseFor;
326import com.openai.models.ChatCompletion;
327import com.openai.models.ChatCompletionCreateParams;
328import com.openai.models.ChatModel;
329
330ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
331 .addUserMessage("Say this is a test")
332 .model(ChatModel.O3_MINI)
333 .build();
334HttpResponseFor<ChatCompletion> chatCompletion = client.chat().completions().withRawResponse().create(params);
335
336int statusCode = chatCompletion.statusCode();
337Headers headers = chatCompletion.headers();
338```
339
340You can still deserialize the response into an instance of a Java class if needed:
341
342```java
343import com.openai.models.ChatCompletion;
344
345ChatCompletion parsedChatCompletion = chatCompletion.parse();
346```
347
348## Error handling
349
350The SDK throws custom unchecked exception types:
351
352- [`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:
353
354 | Status | Exception |
355 | ------ | ------------------------------- |
356 | 400 | `BadRequestException` |
357 | 401 | `AuthenticationException` |
358 | 403 | `PermissionDeniedException` |
359 | 404 | `NotFoundException` |
360 | 422 | `UnprocessableEntityException` |
361 | 429 | `RateLimitException` |
362 | 5xx | `InternalServerException` |
363 | others | `UnexpectedStatusCodeException` |
364
365- [`OpenAIIoException`](openai-java-core/src/main/kotlin/com/openai/errors/OpenAIIoException.kt): I/O networking errors.
366
367- [`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.
368
369- [`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.
370
371## Pagination
372
373For 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.
374
375### Auto-pagination
376
377To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you:
378
379### Synchronous
380
381```java
382import com.openai.models.FineTuningJob;
383import com.openai.models.FineTuningJobListPage;
384
385// As an Iterable:
386FineTuningJobListPage page = client.fineTuning().jobs().list(params);
387for (FineTuningJob job : page.autoPager()) {
388 System.out.println(job);
389};
390
391// As a Stream:
392client.fineTuning().jobs().list(params).autoPager().stream()
393 .limit(50)
394 .forEach(job -> System.out.println(job));
395```
396
397### Asynchronous
398
399```java
400// Using forEach, which returns CompletableFuture<Void>:
401asyncClient.fineTuning().jobs().list(params).autoPager()
402 .forEach(job -> System.out.println(job), executor);
403```
404
405### Manual pagination
406
407If 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.
408
409```java
410import com.openai.models.FineTuningJob;
411import com.openai.models.FineTuningJobListPage;
412
413FineTuningJobListPage page = client.fineTuning().jobs().list(params);
414while (page != null) {
415 for (FineTuningJob job : page.data()) {
416 System.out.println(job);
417 }
418
419 page = page.getNextPage().orElse(null);
420}
421```
422
423## Logging
424
425The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).
426
427Enable logging by setting the `OPENAI_LOG` environment variable to `info`:
428
429```sh
430$ export OPENAI_LOG=info
431```
432
433Or to `debug` for more verbose logging:
434
435```sh
436$ export OPENAI_LOG=debug
437```
438
439## Microsoft Azure
440
441To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same
442OpenAI client builder but with the Azure-specific configuration.
443
444```java
445OpenAIClient client = OpenAIOkHttpClient.builder()
446 // Gets the API key from the `AZURE_OPENAI_KEY` environment variable
447 .fromEnv()
448 // Set the Azure Entra ID
449 .credential(BearerTokenCredential.create(AuthenticationUtil.getBearerTokenSupplier(
450 new DefaultAzureCredentialBuilder().build(), "https://cognitiveservices.azure.com/.default")))
451 .build();
452```
453
454See 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.
455
456## Network options
457
458### Retries
459
460The SDK automatically retries 2 times by default, with a short exponential backoff.
461
462Only the following error types are retried:
463
464- Connection errors (for example, due to a network connectivity problem)
465- 408 Request Timeout
466- 409 Conflict
467- 429 Rate Limit
468- 5xx Internal
469
470The API may also explicitly instruct the SDK to retry or not retry a response.
471
472To set a custom number of retries, configure the client using the `maxRetries` method:
473
474```java
475import com.openai.client.OpenAIClient;
476import com.openai.client.okhttp.OpenAIOkHttpClient;
477
478OpenAIClient client = OpenAIOkHttpClient.builder()
479 .fromEnv()
480 .maxRetries(4)
481 .build();
482```
483
484### Timeouts
485
486Requests time out after 10 minutes by default.
487
488To set a custom timeout, configure the method call using the `timeout` method:
489
490```java
491import com.openai.models.ChatCompletion;
492import com.openai.models.ChatCompletionCreateParams;
493import com.openai.models.ChatModel;
494
495ChatCompletion chatCompletion = client.chat().completions().create(
496 params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()
497);
498```
499
500Or configure the default for all method calls at the client level:
501
502```java
503import com.openai.client.OpenAIClient;
504import com.openai.client.okhttp.OpenAIOkHttpClient;
505import java.time.Duration;
506
507OpenAIClient client = OpenAIOkHttpClient.builder()
508 .fromEnv()
509 .timeout(Duration.ofSeconds(30))
510 .build();
511```
512
513### Proxies
514
515To route requests through a proxy, configure the client using the `proxy` method:
516
517```java
518import com.openai.client.OpenAIClient;
519import com.openai.client.okhttp.OpenAIOkHttpClient;
520import java.net.InetSocketAddress;
521import java.net.Proxy;
522
523OpenAIClient client = OpenAIOkHttpClient.builder()
524 .fromEnv()
525 .proxy(new Proxy(
526 Proxy.Type.HTTP, new InetSocketAddress(
527 "https://example.com", 8080
528 )
529 ))
530 .build();
531```
532
533## Undocumented API functionality
534
535The 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.
536
537### Parameters
538
539To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:
540
541```java
542import com.openai.core.JsonValue;
543import com.openai.models.ChatCompletionCreateParams;
544
545ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
546 .putAdditionalHeader("Secret-Header", "42")
547 .putAdditionalQueryParam("secret_query_param", "42")
548 .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
549 .build();
550```
551
552These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods. You can also set undocumented parameters on nested headers, query params, or body classes using the `putAdditionalProperty` method. These properties can be accessed on the built object later using the `_additionalProperties()` method.
553
554To 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/JsonValue.kt) object to its setter:
555
556```java
557import com.openai.core.JsonValue;
558import com.openai.models.ChatCompletionCreateParams;
559
560ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
561 .addUserMessage("Say this is a test")
562 .model(JsonValue.from(42))
563 .build();
564```
565
566### Response properties
567
568To access undocumented response properties, call the `_additionalProperties()` method:
569
570```java
571import com.openai.core.JsonValue;
572import java.util.Map;
573
574Map<String, JsonValue> additionalProperties = client.chat().completions().create(params)._additionalProperties();
575JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
576
577String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
578 @Override
579 public String visitNull() {
580 return "It's null!";
581 }
582
583 @Override
584 public String visitBoolean(boolean value) {
585 return "It's a boolean!";
586 }
587
588 @Override
589 public String visitNumber(Number value) {
590 return "It's a number!";
591 }
592
593 // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`
594 // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden
595});
596```
597
598To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method:
599
600```java
601import com.openai.core.JsonField;
602import com.openai.models.ChatCompletionMessageParam;
603import java.util.Optional;
604
605JsonField<List<ChatCompletionMessageParam>> messages = client.chat().completions().create(params)._messages();
606
607if (messages.isMissing()) {
608 // The property is absent from the JSON response
609} else if (messages.isNull()) {
610 // The property was set to literal null
611} else {
612 // Check if value was provided as a string
613 // Other methods include `asNumber()`, `asBoolean()`, etc.
614 Optional<String> jsonString = messages.asString();
615
616 // Try to deserialize into a custom type
617 MyClass myObject = messages.asUnknown().orElseThrow().convert(MyClass.class);
618}
619```
620
621### Response validation
622
623In 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.
624
625By 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.
626
627If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:
628
629```java
630import com.openai.models.ChatCompletion;
631
632ChatCompletion chatCompletion = client.chat().completions().create(params).validate();
633```
634
635Or configure the method call to validate the response using the `responseValidation` method:
636
637```java
638import com.openai.models.ChatCompletion;
639import com.openai.models.ChatCompletionCreateParams;
640import com.openai.models.ChatModel;
641
642ChatCompletion chatCompletion = client.chat().completions().create(
643 params, RequestOptions.builder().responseValidation(true).build()
644);
645```
646
647Or configure the default for all method calls at the client level:
648
649```java
650import com.openai.client.OpenAIClient;
651import com.openai.client.okhttp.OpenAIOkHttpClient;
652
653OpenAIClient client = OpenAIOkHttpClient.builder()
654 .fromEnv()
655 .responseValidation(true)
656 .build();
657```
658
659## FAQ
660
661### Why don't you use plain `enum` classes?
662
663Java `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.
664
665### Why do you represent fields using `JsonField<T>` instead of just plain `T`?
666
667Using `JsonField<T>` enables a few features:
668
669- Allowing usage of [undocumented API functionality](#undocumented-api-functionality)
670- Lazily [validating the API response against the expected shape](#response-validation)
671- Representing absent vs explicitly null values
672
673### Why don't you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)?
674
675It 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.
676
677### Why don't you use checked exceptions?
678
679Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.
680
681Checked exceptions:
682
683- Are verbose to handle
684- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error
685- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function)
686- Don't play well with lambdas (also due to the function coloring problem)
687
688## Semantic versioning
689
690This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
691
6921. 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.)_
6932. Changes that we do not expect to impact the vast majority of users in practice.
694
695We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
696
697We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-java/issues) with questions, bugs, or suggestions.
698