@@ -286,7 +286,7 @@ OpenAIClient client = OpenAIOkHttpClient.builder()
286
286
287
287
The SDK provides conveniences for streamed chat completions. A
288
288
[ ` ChatCompletionAccumulator ` ] ( openai-java-core/src/main/kotlin/com/openai/helpers/ChatCompletionAccumulator.kt )
289
- can record the stream of chat completion chunks in the response as they are processed and accumulate
289
+ can record the stream of chat completion chunks in the response as they are processed and accumulate
290
290
a [ ` ChatCompletion ` ] ( openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt )
291
291
object similar to that which would have been returned by the non-streaming API.
292
292
@@ -334,6 +334,205 @@ client.chat()
334
334
ChatCompletion chatCompletion = chatCompletionAccumulator. chatCompletion();
335
335
```
336
336
337
+ ## Structured outputs with JSON schemas
338
+
339
+ Open AI [ Structured Outputs] ( https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat )
340
+ is a feature that ensures that the model will always generate responses that adhere to a supplied
341
+ [ JSON schema] ( https://json-schema.org/overview/what-is-jsonschema ) .
342
+
343
+ A JSON schema can be defined by creating a
344
+ [ ` ResponseFormatJsonSchema ` ] ( openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt )
345
+ and setting it on the input parameters. However, for greater convenience, a JSON schema can instead
346
+ be derived automatically from the structure of an arbitrary Java class. The JSON content from the
347
+ response will then be converted automatically to an instance of that Java class. A full, working
348
+ example of the use of Structured Outputs with arbitrary Java classes can be seen in
349
+ [ ` StructuredOutputsExample ` ] ( openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java ) .
350
+
351
+ Java classes can contain fields declared to be instances of other classes and can use collections:
352
+
353
+ ``` java
354
+ class Person {
355
+ public String name;
356
+ public int birthYear;
357
+ }
358
+
359
+ class Book {
360
+ public String title;
361
+ public Person author;
362
+ public int publicationYear;
363
+ }
364
+
365
+ class BookList {
366
+ public List<Book > books;
367
+ }
368
+ ```
369
+
370
+ Pass the top-level class—` BookList ` in this example—to ` responseFormat(Class<T>) ` when building the
371
+ parameters and then access an instance of ` BookList ` from the generated message content in the
372
+ response:
373
+
374
+ ``` java
375
+ import com.openai.models.ChatModel ;
376
+ import com.openai.models.chat.completions.ChatCompletionCreateParams ;
377
+ import com.openai.models.chat.completions.StructuredChatCompletionCreateParams ;
378
+
379
+ StructuredChatCompletionCreateParams<BookList > params = ChatCompletionCreateParams . builder()
380
+ .addUserMessage(" List some famous late twentieth century novels." )
381
+ .model(ChatModel . GPT_4_1 )
382
+ .responseFormat(BookList . class)
383
+ .build();
384
+
385
+ client. chat(). completions(). create(params). choices(). stream()
386
+ .flatMap(choice - > choice. message(). content(). stream())
387
+ .flatMap(bookList - > bookList. books. stream())
388
+ .forEach(book - > System . out. println(book. title + " by " + book. author. name));
389
+ ```
390
+
391
+ You can start building the parameters with an instance of
392
+ [ ` ChatCompletionCreateParams.Builder ` ] ( openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt )
393
+ or
394
+ [ ` StructuredChatCompletionCreateParams.Builder ` ] ( openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt ) .
395
+ If you start with the former (which allows for more compact code) the builder type will change to
396
+ the latter when ` ChatCompletionCreateParams.Builder.responseFormat(Class<T>) ` is called.
397
+
398
+ If a field in a class is optional and does not require a defined value, you can represent this using
399
+ the [ ` java.util.Optional ` ] ( https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html ) class.
400
+ It is up to the AI model to decide whether to provide a value for that field or leave it empty.
401
+
402
+ ``` java
403
+ import java.util.Optional ;
404
+
405
+ class Book {
406
+ public String title;
407
+ public Person author;
408
+ public int publicationYear;
409
+ public Optional<String > isbn;
410
+ }
411
+ ```
412
+
413
+ Generic type information for fields is retained in the class's metadata, but _ generic type erasure_
414
+ applies in other scopes. While, for example, a JSON schema defining an array of books can be derived
415
+ from the ` BookList.books ` field with type ` List<Book> ` , a valid JSON schema cannot be derived from a
416
+ local variable of that same type, so the following will _ not_ work:
417
+
418
+ ``` java
419
+ List<Book > books = new ArrayList<> ();
420
+
421
+ StructuredChatCompletionCreateParams<List<Book > > params = ChatCompletionCreateParams . builder()
422
+ .responseFormat(books. getClass())
423
+ // ...
424
+ .build();
425
+ ```
426
+
427
+ If an error occurs while converting a JSON response to an instance of a Java class, the error
428
+ message will include the JSON response to assist in diagnosis. For instance, if the response is
429
+ truncated, the JSON data will be incomplete and cannot be converted to a class instance. If your
430
+ JSON response may contain sensitive information, avoid logging it directly, or ensure that you
431
+ redact any sensitive details from the error message.
432
+
433
+ ### Local JSON schema validation
434
+
435
+ Structured Outputs supports a
436
+ [ subset] ( https://platform.openai.com/docs/guides/structured-outputs#supported-schemas ) of the JSON
437
+ Schema language. Schemas are generated automatically from classes to align with this subset.
438
+ However, due to the inherent structure of the classes, the generated schema may still violate
439
+ certain OpenAI schema restrictions, such as exceeding the maximum nesting depth or utilizing
440
+ unsupported data types.
441
+
442
+ To facilitate compliance, the method ` responseFormat(Class<T>) ` performs a validation check on the
443
+ schema derived from the specified class. This validation ensures that all restrictions are adhered
444
+ to. If any issues are detected, an exception will be thrown, providing a detailed message outlining
445
+ the reasons for the validation failure.
446
+
447
+ - ** Local Validation** : The validation process occurs locally, meaning no requests are sent to the
448
+ remote AI model. If the schema passes local validation, it is likely to pass remote validation as
449
+ well.
450
+ - ** Remote Validation** : The remote AI model will conduct its own validation upon receiving the JSON
451
+ schema in the request.
452
+ - ** Version Compatibility** : There may be instances where local validation fails while remote
453
+ validation succeeds. This can occur if the SDK version is outdated compared to the restrictions
454
+ enforced by the remote AI model.
455
+ - ** Disabling Local Validation** : If you encounter compatibility issues and wish to bypass local
456
+ validation, you can disable it by passing
457
+ [ ` JsonSchemaLocalValidation.NO ` ] ( openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaLocalValidation.kt )
458
+ to the ` responseFormat(Class<T>, JsonSchemaLocalValidation) ` method when building the parameters.
459
+ (The default value for this parameter is ` JsonSchemaLocalValidation.YES ` .)
460
+
461
+ ``` java
462
+ import com.openai.core.JsonSchemaLocalValidation ;
463
+ import com.openai.models.ChatModel ;
464
+ import com.openai.models.chat.completions.ChatCompletionCreateParams ;
465
+ import com.openai.models.chat.completions.StructuredChatCompletionCreateParams ;
466
+
467
+ StructuredChatCompletionCreateParams<BookList > params = ChatCompletionCreateParams . builder()
468
+ .addUserMessage(" List some famous late twentieth century novels." )
469
+ .model(ChatModel . GPT_4_1 )
470
+ .responseFormat(BookList . class, JsonSchemaLocalValidation . NO )
471
+ .build();
472
+ ```
473
+
474
+ By following these guidelines, you can ensure that your structured outputs conform to the necessary
475
+ schema requirements and minimize the risk of remote validation errors.
476
+
477
+ ### Usage with the Responses API
478
+
479
+ _ Structured Outputs_ are also supported for the Responses API. The usage is the same as described
480
+ except where the Responses API differs slightly from the Chat Completions API. Pass the top-level
481
+ class to ` text(Class<T>) ` when building the parameters and then access an instance of the class from
482
+ the generated message content in the response.
483
+
484
+ You can start building the parameters with an instance of
485
+ [ ` ResponseCreateParams.Builder ` ] ( openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt )
486
+ or
487
+ [ ` StructuredResponseCreateParams.Builder ` ] ( openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt ) .
488
+ If you start with the former (which allows for more compact code) the builder type will change to
489
+ the latter when ` ResponseCreateParams.Builder.text(Class<T>) ` is called.
490
+
491
+ For a full example of the usage of _ Structured Outputs_ with the Responses API, see
492
+ [ ` ResponsesStructuredOutputsExample ` ] ( openai-java-example/src/main/java/com/openai/example/ResponsesStructuredOutputsExample.java ) .
493
+
494
+ ### Annotating classes and JSON schemas
495
+
496
+ You can use annotations to add further information to the JSON schema derived from your Java
497
+ classes, or to exclude individual fields from the schema. Details from annotations captured in the
498
+ JSON schema may be used by the AI model to improve its response. The SDK supports the use of
499
+ [ Jackson Databind] ( https://github.com/FasterXML/jackson-databind ) annotations.
500
+
501
+ ``` java
502
+ import com.fasterxml.jackson.annotation.JsonClassDescription ;
503
+ import com.fasterxml.jackson.annotation.JsonIgnore ;
504
+ import com.fasterxml.jackson.annotation.JsonPropertyDescription ;
505
+
506
+ class Person {
507
+ @JsonPropertyDescription (" The first name and surname of the person" )
508
+ public String name;
509
+ public int birthYear;
510
+ @JsonPropertyDescription (" The year the person died, or 'present' if the person is living." )
511
+ public String deathYear;
512
+ }
513
+
514
+ @JsonClassDescription (" The details of one published book" )
515
+ class Book {
516
+ public String title;
517
+ public Person author;
518
+ @JsonPropertyDescription (" The year in which the book was first published." )
519
+ public int publicationYear;
520
+ @JsonIgnore public String genre;
521
+ }
522
+
523
+ class BookList {
524
+ public List<Book > books;
525
+ }
526
+ ```
527
+
528
+ - Use ` @JsonClassDescription ` to add a detailed description to a class.
529
+ - Use ` @JsonPropertyDescription ` to add a detailed description to a field of a class.
530
+ - Use ` @JsonIgnore ` to omit a field of a class from the generated JSON schema.
531
+
532
+ If you use ` @JsonProperty(required = false) ` , the ` false ` value will be ignored. OpenAI JSON schemas
533
+ must mark all properties as _ required_ , so the schema generated from your Java classes will respect
534
+ that restriction and ignore any annotation that would violate it.
535
+
337
536
## File uploads
338
537
339
538
The SDK defines methods that accept files.
@@ -652,7 +851,7 @@ If the SDK threw an exception, but you're _certain_ the version is compatible, t
652
851
653
852
## Microsoft Azure
654
853
655
- To use this library with [ Azure OpenAI] ( https://learn.microsoft.com/azure/ai-services/openai/overview ) , use the same
854
+ To use this library with [ Azure OpenAI] ( https://learn.microsoft.com/azure/ai-services/openai/overview ) , use the same
656
855
OpenAI client builder but with the Azure-specific configuration.
657
856
658
857
``` java
@@ -665,7 +864,7 @@ OpenAIClient client = OpenAIOkHttpClient.builder()
665
864
.build();
666
865
```
667
866
668
- See 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.
867
+ See 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.
669
868
670
869
## Network options
671
870
0 commit comments