From 286b7d52d8cb917130579352a89afd5f8c839894 Mon Sep 17 00:00:00 2001 From: nuclear bean Date: Wed, 30 Apr 2025 19:18:40 +0200 Subject: [PATCH 1/4] docs: add `ImageGenerationExample` (#457) * ImageGenerationExample * docs: slight example tweaks --------- Co-authored-by: Tomer Aberbach --- .../example/ImageGenerationExample.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 openai-java-example/src/main/java/com/openai/example/ImageGenerationExample.java diff --git a/openai-java-example/src/main/java/com/openai/example/ImageGenerationExample.java b/openai-java-example/src/main/java/com/openai/example/ImageGenerationExample.java new file mode 100644 index 00000000..bb9369b9 --- /dev/null +++ b/openai-java-example/src/main/java/com/openai/example/ImageGenerationExample.java @@ -0,0 +1,30 @@ +package com.openai.example; + +import com.openai.client.OpenAIClient; +import com.openai.client.okhttp.OpenAIOkHttpClient; +import com.openai.models.images.ImageGenerateParams; +import com.openai.models.images.ImageModel; +import java.io.IOException; + +public final class ImageGenerationExample { + private ImageGenerationExample() {} + + public static void main(String[] args) throws IOException { + // Configures using one of: + // - The `OPENAI_API_KEY` environment variable + // - The `OPENAI_BASE_URL` and `AZURE_OPENAI_KEY` environment variables + OpenAIClient client = OpenAIOkHttpClient.fromEnv(); + + ImageGenerateParams imageGenerateParams = ImageGenerateParams.builder() + .responseFormat(ImageGenerateParams.ResponseFormat.URL) + .prompt("Two cats playing ping-pong") + .model(ImageModel.DALL_E_2) + .size(ImageGenerateParams.Size._512X512) + .n(1) + .build(); + + client.images().generate(imageGenerateParams).data().orElseThrow().stream() + .flatMap(image -> image.url().stream()) + .forEach(System.out::println); + } +} From eef415d067c9dd6ac6d4b8c825f4d068a6c4a63c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 00:16:40 +0000 Subject: [PATCH 2/4] chore(internal): update java toolchain --- buildSrc/src/main/kotlin/openai.java.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/openai.java.gradle.kts b/buildSrc/src/main/kotlin/openai.java.gradle.kts index e39d9ac6..dfbacb86 100644 --- a/buildSrc/src/main/kotlin/openai.java.gradle.kts +++ b/buildSrc/src/main/kotlin/openai.java.gradle.kts @@ -21,7 +21,7 @@ configure { java { toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(21)) } sourceCompatibility = JavaVersion.VERSION_1_8 From a4a7d5390245ba11afe1cbd8665e0e39af3ca16f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 19:13:08 +0000 Subject: [PATCH 3/4] feat(api): add image sizes, reasoning encryption --- .stats.yml | 6 +- .../models/audio/speech/SpeechCreateParams.kt | 7 +- .../openai/models/images/ImageEditParams.kt | 348 ++++++++++++++++-- .../openai/models/responses/ComputerTool.kt | 50 +-- .../openai/models/responses/FileSearchTool.kt | 11 +- .../openai/models/responses/FunctionTool.kt | 31 +- .../com/openai/models/responses/Response.kt | 6 +- .../models/responses/ResponseCreateParams.kt | 31 +- .../models/responses/ResponseIncludable.kt | 10 + .../models/responses/ResponseInputFile.kt | 6 +- .../models/responses/ResponseInputImage.kt | 12 +- .../models/responses/ResponseInputItem.kt | 258 ++++++++++--- .../models/responses/ResponseOutputItem.kt | 14 +- .../models/responses/ResponseReasoningItem.kt | 65 +++- .../com/openai/models/responses/Tool.kt | 81 ++-- .../openai/models/responses/WebSearchTool.kt | 39 +- .../models/images/ImageEditParamsTest.kt | 3 + .../models/responses/ComputerToolTest.kt | 18 +- .../models/responses/FunctionToolTest.kt | 4 +- .../models/responses/ResponseContentTest.kt | 4 +- .../responses/ResponseInputContentTest.kt | 4 +- .../responses/ResponseInputImageTest.kt | 6 +- .../models/responses/ResponseInputItemTest.kt | 21 +- .../responses/ResponseOutputItemTest.kt | 2 + .../responses/ResponseReasoningItemTest.kt | 3 + .../com/openai/models/responses/ToolTest.kt | 76 ++-- .../services/async/ImageServiceAsyncTest.kt | 1 + .../services/blocking/ImageServiceTest.kt | 1 + 28 files changed, 852 insertions(+), 266 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7738ef3d..089abe5d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 95 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-8b68ae6b807dca92e914da1dd9e835a20f69b075e79102a264367fd7fddddb33.yml -openapi_spec_hash: b6ade5b1a6327339e6669e1134de2d03 -config_hash: b597cd9a31e9e5ec709e2eefb4c54122 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0ee6b36cf3cc278cef4199a6aec5f7d530a6c1f17a74830037e96d50ca1edc50.yml +openapi_spec_hash: e8ec5f46bc0655b34f292422d58a60f6 +config_hash: d9b6b6e6bc85744663e300eebc482067 diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/speech/SpeechCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/speech/SpeechCreateParams.kt index 8aa93d64..3b79ac40 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/speech/SpeechCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/speech/SpeechCreateParams.kt @@ -76,6 +76,7 @@ private constructor( /** * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. + * Does not work with `gpt-4o-mini-tts`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -271,7 +272,7 @@ private constructor( /** * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the - * default. + * default. Does not work with `gpt-4o-mini-tts`. */ fun speed(speed: Double) = apply { body.speed(speed) } @@ -502,7 +503,7 @@ private constructor( /** * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the - * default. + * default. Does not work with `gpt-4o-mini-tts`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -705,7 +706,7 @@ private constructor( /** * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the - * default. + * default. Does not work with `gpt-4o-mini-tts`. */ fun speed(speed: Double) = speed(JsonField.of(speed)) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt index 3fb030f9..b5a9254d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt @@ -46,10 +46,13 @@ private constructor( ) : Params { /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. For - * `dall-e-2`, you can only provide one image, and it should be a square `png` file less than - * 4MB. + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. You + * can provide up to 16 images. + * + * For `dall-e-2`, you can only provide one image, and it should be a square `png` file less + * than 4MB. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -65,6 +68,20 @@ private constructor( */ fun prompt(): String = body.prompt() + /** + * Allows to set transparency for the background of the generated image(s). This parameter is + * only supported for `gpt-image-1`. Must be one of `transparent`, `opaque` or `auto` (default + * value). When `auto` is used, the model will automatically determine the best background for + * the image. + * + * If `transparent`, the output format needs to support transparency, so it should be set to + * either `png` (default value) or `webp`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun background(): Optional = body.background() + /** * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where * `image` should be edited. If there are multiple images provided, the mask will be applied on @@ -146,6 +163,13 @@ private constructor( */ fun _prompt(): MultipartField = body._prompt() + /** + * Returns the raw multipart value of [background]. + * + * Unlike [background], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _background(): MultipartField = body._background() + /** * Returns the raw multipart value of [mask]. * @@ -237,17 +261,20 @@ private constructor( * Otherwise, it's more convenient to use the top-level setters instead: * - [image] * - [prompt] + * - [background] * - [mask] * - [model] - * - [n] * - etc. */ fun body(body: Body) = apply { this.body = body.toBuilder() } /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. For - * `dall-e-2`, you can only provide one image, and it should be a square `png` file less + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * You can provide up to 16 images. + * + * For `dall-e-2`, you can only provide one image, and it should be a square `png` file less * than 4MB. */ fun image(image: Image) = apply { body.image(image) } @@ -264,17 +291,23 @@ private constructor( fun image(inputStream: InputStream) = apply { body.image(inputStream) } /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. For - * `dall-e-2`, you can only provide one image, and it should be a square `png` file less + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * You can provide up to 16 images. + * + * For `dall-e-2`, you can only provide one image, and it should be a square `png` file less * than 4MB. */ fun image(inputStream: ByteArray) = apply { body.image(inputStream) } /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. For - * `dall-e-2`, you can only provide one image, and it should be a square `png` file less + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * You can provide up to 16 images. + * + * For `dall-e-2`, you can only provide one image, and it should be a square `png` file less * than 4MB. */ fun image(inputStream: Path) = apply { body.image(inputStream) } @@ -298,6 +331,31 @@ private constructor( */ fun prompt(prompt: MultipartField) = apply { body.prompt(prompt) } + /** + * Allows to set transparency for the background of the generated image(s). This parameter + * is only supported for `gpt-image-1`. Must be one of `transparent`, `opaque` or `auto` + * (default value). When `auto` is used, the model will automatically determine the best + * background for the image. + * + * If `transparent`, the output format needs to support transparency, so it should be set to + * either `png` (default value) or `webp`. + */ + fun background(background: Background?) = apply { body.background(background) } + + /** Alias for calling [Builder.background] with `background.orElse(null)`. */ + fun background(background: Optional) = background(background.getOrNull()) + + /** + * Sets [Builder.background] to an arbitrary multipart value. + * + * You should usually call [Builder.background] with a well-typed [Background] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun background(background: MultipartField) = apply { + body.background(background) + } + /** * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate * where `image` should be edited. If there are multiple images provided, the mask will be @@ -573,6 +631,7 @@ private constructor( mapOf( "image" to _image(), "prompt" to _prompt(), + "background" to _background(), "mask" to _mask(), "model" to _model(), "n" to _n(), @@ -591,6 +650,7 @@ private constructor( private constructor( private val image: MultipartField, private val prompt: MultipartField, + private val background: MultipartField, private val mask: MultipartField, private val model: MultipartField, private val n: MultipartField, @@ -601,9 +661,12 @@ private constructor( ) { /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. For - * `dall-e-2`, you can only provide one image, and it should be a square `png` file less + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * You can provide up to 16 images. + * + * For `dall-e-2`, you can only provide one image, and it should be a square `png` file less * than 4MB. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -620,6 +683,20 @@ private constructor( */ fun prompt(): String = prompt.value.getRequired("prompt") + /** + * Allows to set transparency for the background of the generated image(s). This parameter + * is only supported for `gpt-image-1`. Must be one of `transparent`, `opaque` or `auto` + * (default value). When `auto` is used, the model will automatically determine the best + * background for the image. + * + * If `transparent`, the output format needs to support transparency, so it should be set to + * either `png` (default value) or `webp`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun background(): Optional = background.value.getOptional("background") + /** * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate * where `image` should be edited. If there are multiple images provided, the mask will be @@ -704,6 +781,16 @@ private constructor( */ @JsonProperty("prompt") @ExcludeMissing fun _prompt(): MultipartField = prompt + /** + * Returns the raw multipart value of [background]. + * + * Unlike [background], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("background") + @ExcludeMissing + fun _background(): MultipartField = background + /** * Returns the raw multipart value of [mask]. * @@ -778,6 +865,7 @@ private constructor( private var image: MultipartField? = null private var prompt: MultipartField? = null + private var background: MultipartField = MultipartField.of(null) private var mask: MultipartField = MultipartField.of(null) private var model: MultipartField = MultipartField.of(null) private var n: MultipartField = MultipartField.of(null) @@ -790,6 +878,7 @@ private constructor( internal fun from(body: Body) = apply { image = body.image prompt = body.prompt + background = body.background mask = body.mask model = body.model n = body.n @@ -800,8 +889,11 @@ private constructor( } /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + * 25MB. You can provide up to 16 images. + * * For `dall-e-2`, you can only provide one image, and it should be a square `png` file * less than 4MB. */ @@ -820,16 +912,22 @@ private constructor( fun image(inputStream: InputStream) = image(Image.ofInputStream(inputStream)) /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + * 25MB. You can provide up to 16 images. + * * For `dall-e-2`, you can only provide one image, and it should be a square `png` file * less than 4MB. */ fun image(inputStream: ByteArray) = image(inputStream.inputStream()) /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + * 25MB. You can provide up to 16 images. + * * For `dall-e-2`, you can only provide one image, and it should be a square `png` file * less than 4MB. */ @@ -860,6 +958,31 @@ private constructor( */ fun prompt(prompt: MultipartField) = apply { this.prompt = prompt } + /** + * Allows to set transparency for the background of the generated image(s). This + * parameter is only supported for `gpt-image-1`. Must be one of `transparent`, `opaque` + * or `auto` (default value). When `auto` is used, the model will automatically + * determine the best background for the image. + * + * If `transparent`, the output format needs to support transparency, so it should be + * set to either `png` (default value) or `webp`. + */ + fun background(background: Background?) = background(MultipartField.of(background)) + + /** Alias for calling [Builder.background] with `background.orElse(null)`. */ + fun background(background: Optional) = background(background.getOrNull()) + + /** + * Sets [Builder.background] to an arbitrary multipart value. + * + * You should usually call [Builder.background] with a well-typed [Background] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun background(background: MultipartField) = apply { + this.background = background + } + /** * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate * where `image` should be edited. If there are multiple images provided, the mask will @@ -1044,6 +1167,7 @@ private constructor( Body( checkRequired("image", image), checkRequired("prompt", prompt), + background, mask, model, n, @@ -1063,6 +1187,7 @@ private constructor( image().validate() prompt() + background().ifPresent { it.validate() } mask() model().ifPresent { it.validate() } n() @@ -1086,24 +1211,27 @@ private constructor( return true } - return /* spotless:off */ other is Body && image == other.image && prompt == other.prompt && mask == other.mask && model == other.model && n == other.n && quality == other.quality && responseFormat == other.responseFormat && size == other.size && user == other.user /* spotless:on */ + return /* spotless:off */ other is Body && image == other.image && prompt == other.prompt && background == other.background && mask == other.mask && model == other.model && n == other.n && quality == other.quality && responseFormat == other.responseFormat && size == other.size && user == other.user /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(image, prompt, mask, model, n, quality, responseFormat, size, user) } + private val hashCode: Int by lazy { Objects.hash(image, prompt, background, mask, model, n, quality, responseFormat, size, user) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Body{image=$image, prompt=$prompt, mask=$mask, model=$model, n=$n, quality=$quality, responseFormat=$responseFormat, size=$size, user=$user}" + "Body{image=$image, prompt=$prompt, background=$background, mask=$mask, model=$model, n=$n, quality=$quality, responseFormat=$responseFormat, size=$size, user=$user}" } /** - * The image(s) to edit. Must be a supported image file or an array of images. For - * `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. For - * `dall-e-2`, you can only provide one image, and it should be a square `png` file less than - * 4MB. + * The image(s) to edit. Must be a supported image file or an array of images. + * + * For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than 25MB. You + * can provide up to 16 images. + * + * For `dall-e-2`, you can only provide one image, and it should be a square `png` file less + * than 4MB. */ @JsonDeserialize(using = Image.Deserializer::class) @JsonSerialize(using = Image.Serializer::class) @@ -1275,6 +1403,148 @@ private constructor( } } + /** + * Allows to set transparency for the background of the generated image(s). This parameter is + * only supported for `gpt-image-1`. Must be one of `transparent`, `opaque` or `auto` (default + * value). When `auto` is used, the model will automatically determine the best background for + * the image. + * + * If `transparent`, the output format needs to support transparency, so it should be set to + * either `png` (default value) or `webp`. + */ + class Background @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val TRANSPARENT = of("transparent") + + @JvmField val OPAQUE = of("opaque") + + @JvmField val AUTO = of("auto") + + @JvmStatic fun of(value: String) = Background(JsonField.of(value)) + } + + /** An enum containing [Background]'s known values. */ + enum class Known { + TRANSPARENT, + OPAQUE, + AUTO, + } + + /** + * An enum containing [Background]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Background] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + TRANSPARENT, + OPAQUE, + AUTO, + /** + * An enum member indicating that [Background] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + TRANSPARENT -> Value.TRANSPARENT + OPAQUE -> Value.OPAQUE + AUTO -> Value.AUTO + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + TRANSPARENT -> Known.TRANSPARENT + OPAQUE -> Known.OPAQUE + AUTO -> Known.AUTO + else -> throw OpenAIInvalidDataException("Unknown Background: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Background = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Background && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + /** * The quality of the image that will be generated. `high`, `medium` and `low` are only * supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -1581,6 +1851,12 @@ private constructor( @JvmField val _1024X1024 = of("1024x1024") + @JvmField val _1536X1024 = of("1536x1024") + + @JvmField val _1024X1536 = of("1024x1536") + + @JvmField val AUTO = of("auto") + @JvmStatic fun of(value: String) = Size(JsonField.of(value)) } @@ -1589,6 +1865,9 @@ private constructor( _256X256, _512X512, _1024X1024, + _1536X1024, + _1024X1536, + AUTO, } /** @@ -1604,6 +1883,9 @@ private constructor( _256X256, _512X512, _1024X1024, + _1536X1024, + _1024X1536, + AUTO, /** An enum member indicating that [Size] was instantiated with an unknown value. */ _UNKNOWN, } @@ -1620,6 +1902,9 @@ private constructor( _256X256 -> Value._256X256 _512X512 -> Value._512X512 _1024X1024 -> Value._1024X1024 + _1536X1024 -> Value._1536X1024 + _1024X1536 -> Value._1024X1536 + AUTO -> Value.AUTO else -> Value._UNKNOWN } @@ -1637,6 +1922,9 @@ private constructor( _256X256 -> Known._256X256 _512X512 -> Known._512X512 _1024X1024 -> Known._1024X1024 + _1536X1024 -> Known._1536X1024 + _1024X1536 -> Known._1024X1536 + AUTO -> Known.AUTO else -> throw OpenAIInvalidDataException("Unknown Size: $value") } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt index 486080c4..3ce816ca 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt @@ -23,8 +23,8 @@ import kotlin.jvm.optionals.getOrNull */ class ComputerTool private constructor( - private val displayHeight: JsonField, - private val displayWidth: JsonField, + private val displayHeight: JsonField, + private val displayWidth: JsonField, private val environment: JsonField, private val type: JsonValue, private val additionalProperties: MutableMap, @@ -34,10 +34,10 @@ private constructor( private constructor( @JsonProperty("display_height") @ExcludeMissing - displayHeight: JsonField = JsonMissing.of(), + displayHeight: JsonField = JsonMissing.of(), @JsonProperty("display_width") @ExcludeMissing - displayWidth: JsonField = JsonMissing.of(), + displayWidth: JsonField = JsonMissing.of(), @JsonProperty("environment") @ExcludeMissing environment: JsonField = JsonMissing.of(), @@ -50,7 +50,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun displayHeight(): Double = displayHeight.getRequired("display_height") + fun displayHeight(): Long = displayHeight.getRequired("display_height") /** * The width of the computer display. @@ -58,7 +58,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun displayWidth(): Double = displayWidth.getRequired("display_width") + fun displayWidth(): Long = displayWidth.getRequired("display_width") /** * The type of computer environment to control. @@ -88,7 +88,7 @@ private constructor( */ @JsonProperty("display_height") @ExcludeMissing - fun _displayHeight(): JsonField = displayHeight + fun _displayHeight(): JsonField = displayHeight /** * Returns the raw JSON value of [displayWidth]. @@ -97,7 +97,7 @@ private constructor( */ @JsonProperty("display_width") @ExcludeMissing - fun _displayWidth(): JsonField = displayWidth + fun _displayWidth(): JsonField = displayWidth /** * Returns the raw JSON value of [environment]. @@ -138,8 +138,8 @@ private constructor( /** A builder for [ComputerTool]. */ class Builder internal constructor() { - private var displayHeight: JsonField? = null - private var displayWidth: JsonField? = null + private var displayHeight: JsonField? = null + private var displayWidth: JsonField? = null private var environment: JsonField? = null private var type: JsonValue = JsonValue.from("computer_use_preview") private var additionalProperties: MutableMap = mutableMapOf() @@ -154,32 +154,30 @@ private constructor( } /** The height of the computer display. */ - fun displayHeight(displayHeight: Double) = displayHeight(JsonField.of(displayHeight)) + fun displayHeight(displayHeight: Long) = displayHeight(JsonField.of(displayHeight)) /** * Sets [Builder.displayHeight] to an arbitrary JSON value. * - * You should usually call [Builder.displayHeight] with a well-typed [Double] value instead. + * You should usually call [Builder.displayHeight] with a well-typed [Long] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun displayHeight(displayHeight: JsonField) = apply { + fun displayHeight(displayHeight: JsonField) = apply { this.displayHeight = displayHeight } /** The width of the computer display. */ - fun displayWidth(displayWidth: Double) = displayWidth(JsonField.of(displayWidth)) + fun displayWidth(displayWidth: Long) = displayWidth(JsonField.of(displayWidth)) /** * Sets [Builder.displayWidth] to an arbitrary JSON value. * - * You should usually call [Builder.displayWidth] with a well-typed [Double] value instead. + * You should usually call [Builder.displayWidth] with a well-typed [Long] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun displayWidth(displayWidth: JsonField) = apply { - this.displayWidth = displayWidth - } + fun displayWidth(displayWidth: JsonField) = apply { this.displayWidth = displayWidth } /** The type of computer environment to control. */ fun environment(environment: Environment) = environment(JsonField.of(environment)) @@ -306,9 +304,11 @@ private constructor( companion object { + @JvmField val WINDOWS = of("windows") + @JvmField val MAC = of("mac") - @JvmField val WINDOWS = of("windows") + @JvmField val LINUX = of("linux") @JvmField val UBUNTU = of("ubuntu") @@ -319,8 +319,9 @@ private constructor( /** An enum containing [Environment]'s known values. */ enum class Known { - MAC, WINDOWS, + MAC, + LINUX, UBUNTU, BROWSER, } @@ -335,8 +336,9 @@ private constructor( * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - MAC, WINDOWS, + MAC, + LINUX, UBUNTU, BROWSER, /** @@ -354,8 +356,9 @@ private constructor( */ fun value(): Value = when (this) { - MAC -> Value.MAC WINDOWS -> Value.WINDOWS + MAC -> Value.MAC + LINUX -> Value.LINUX UBUNTU -> Value.UBUNTU BROWSER -> Value.BROWSER else -> Value._UNKNOWN @@ -372,8 +375,9 @@ private constructor( */ fun known(): Known = when (this) { - MAC -> Known.MAC WINDOWS -> Known.WINDOWS + MAC -> Known.MAC + LINUX -> Known.LINUX UBUNTU -> Known.UBUNTU BROWSER -> Known.BROWSER else -> throw OpenAIInvalidDataException("Unknown Environment: $value") diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt index 64036176..5e16106e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt @@ -84,7 +84,7 @@ private constructor( fun vectorStoreIds(): List = vectorStoreIds.getRequired("vector_store_ids") /** - * A filter to apply based on file attributes. + * A filter to apply. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -227,8 +227,11 @@ private constructor( } } - /** A filter to apply based on file attributes. */ - fun filters(filters: Filters) = filters(JsonField.of(filters)) + /** A filter to apply. */ + fun filters(filters: Filters?) = filters(JsonField.ofNullable(filters)) + + /** Alias for calling [Builder.filters] with `filters.orElse(null)`. */ + fun filters(filters: Optional) = filters(filters.getOrNull()) /** * Sets [Builder.filters] to an arbitrary JSON value. @@ -360,7 +363,7 @@ private constructor( (if (maxNumResults.asKnown().isPresent) 1 else 0) + (rankingOptions.asKnown().getOrNull()?.validity() ?: 0) - /** A filter to apply based on file attributes. */ + /** A filter to apply. */ @JsonDeserialize(using = Filters.Deserializer::class) @JsonSerialize(using = Filters.Serializer::class) class Filters diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt index babd1655..f045bfdd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt @@ -56,18 +56,18 @@ private constructor( /** * A JSON schema object describing the parameters of the function. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). */ - fun parameters(): Parameters = parameters.getRequired("parameters") + fun parameters(): Optional = parameters.getOptional("parameters") /** * Whether to enforce strict parameter validation. Default `true`. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). */ - fun strict(): Boolean = strict.getRequired("strict") + fun strict(): Optional = strict.getOptional("strict") /** * The type of the function tool. Always `function`. @@ -180,7 +180,10 @@ private constructor( fun name(name: JsonField) = apply { this.name = name } /** A JSON schema object describing the parameters of the function. */ - fun parameters(parameters: Parameters) = parameters(JsonField.of(parameters)) + fun parameters(parameters: Parameters?) = parameters(JsonField.ofNullable(parameters)) + + /** Alias for calling [Builder.parameters] with `parameters.orElse(null)`. */ + fun parameters(parameters: Optional) = parameters(parameters.getOrNull()) /** * Sets [Builder.parameters] to an arbitrary JSON value. @@ -192,7 +195,17 @@ private constructor( fun parameters(parameters: JsonField) = apply { this.parameters = parameters } /** Whether to enforce strict parameter validation. Default `true`. */ - fun strict(strict: Boolean) = strict(JsonField.of(strict)) + fun strict(strict: Boolean?) = strict(JsonField.ofNullable(strict)) + + /** + * Alias for [Builder.strict]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun strict(strict: Boolean) = strict(strict as Boolean?) + + /** Alias for calling [Builder.strict] with `strict.orElse(null)`. */ + fun strict(strict: Optional) = strict(strict.getOrNull()) /** * Sets [Builder.strict] to an arbitrary JSON value. @@ -286,7 +299,7 @@ private constructor( } name() - parameters().validate() + parameters().ifPresent { it.validate() } strict() _type().let { if (it != JsonValue.from("function")) { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt index e7e23c7e..0380d865 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt @@ -985,13 +985,13 @@ private constructor( /** Alias for calling [addTool] with `Tool.ofFunction(function)`. */ fun addTool(function: FunctionTool) = addTool(Tool.ofFunction(function)) + /** Alias for calling [addTool] with `Tool.ofWebSearch(webSearch)`. */ + fun addTool(webSearch: WebSearchTool) = addTool(Tool.ofWebSearch(webSearch)) + /** Alias for calling [addTool] with `Tool.ofComputerUsePreview(computerUsePreview)`. */ fun addTool(computerUsePreview: ComputerTool) = addTool(Tool.ofComputerUsePreview(computerUsePreview)) - /** Alias for calling [addTool] with `Tool.ofWebSearch(webSearch)`. */ - fun addTool(webSearch: WebSearchTool) = addTool(Tool.ofWebSearch(webSearch)) - /** * An alternative to sampling with temperature, called nucleus sampling, where the model * considers the results of the tokens with top_p probability mass. So 0.1 means only the diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt index 356ccca0..6b512ed9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt @@ -87,6 +87,10 @@ private constructor( * - `file_search_call.results`: Include the search results of the file search tool call. * - `message.input_image.image_url`: Include image urls from the input message. * - `computer_call_output.output.image_url`: Include image urls from the computer call output. + * - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in + * reasoning item outputs. This enables reasoning items to be used in multi-turn conversations + * when using the Responses API statelessly (like when the `store` parameter is set to + * `false`, or when an organization is enrolled in the zero data retention program). * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -507,6 +511,11 @@ private constructor( * - `message.input_image.image_url`: Include image urls from the input message. * - `computer_call_output.output.image_url`: Include image urls from the computer call * output. + * - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in + * reasoning item outputs. This enables reasoning items to be used in multi-turn + * conversations when using the Responses API statelessly (like when the `store` parameter + * is set to `false`, or when an organization is enrolled in the zero data retention + * program). */ fun include(include: List?) = apply { body.include(include) } @@ -849,12 +858,12 @@ private constructor( /** Alias for calling [addTool] with `Tool.ofFunction(function)`. */ fun addTool(function: FunctionTool) = apply { body.addTool(function) } - /** Alias for calling [addTool] with `Tool.ofComputerUsePreview(computerUsePreview)`. */ - fun addTool(computerUsePreview: ComputerTool) = apply { body.addTool(computerUsePreview) } - /** Alias for calling [addTool] with `Tool.ofWebSearch(webSearch)`. */ fun addTool(webSearch: WebSearchTool) = apply { body.addTool(webSearch) } + /** Alias for calling [addTool] with `Tool.ofComputerUsePreview(computerUsePreview)`. */ + fun addTool(computerUsePreview: ComputerTool) = apply { body.addTool(computerUsePreview) } + /** * An alternative to sampling with temperature, called nucleus sampling, where the model * considers the results of the tokens with top_p probability mass. So 0.1 means only the @@ -1187,6 +1196,11 @@ private constructor( * - `message.input_image.image_url`: Include image urls from the input message. * - `computer_call_output.output.image_url`: Include image urls from the computer call * output. + * - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in + * reasoning item outputs. This enables reasoning items to be used in multi-turn + * conversations when using the Responses API statelessly (like when the `store` parameter + * is set to `false`, or when an organization is enrolled in the zero data retention + * program). * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -1656,6 +1670,11 @@ private constructor( * - `message.input_image.image_url`: Include image urls from the input message. * - `computer_call_output.output.image_url`: Include image urls from the computer call * output. + * - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in + * reasoning item outputs. This enables reasoning items to be used in multi-turn + * conversations when using the Responses API statelessly (like when the `store` + * parameter is set to `false`, or when an organization is enrolled in the zero data + * retention program). */ fun include(include: List?) = include(JsonField.ofNullable(include)) @@ -2022,13 +2041,13 @@ private constructor( /** Alias for calling [addTool] with `Tool.ofFunction(function)`. */ fun addTool(function: FunctionTool) = addTool(Tool.ofFunction(function)) + /** Alias for calling [addTool] with `Tool.ofWebSearch(webSearch)`. */ + fun addTool(webSearch: WebSearchTool) = addTool(Tool.ofWebSearch(webSearch)) + /** Alias for calling [addTool] with `Tool.ofComputerUsePreview(computerUsePreview)`. */ fun addTool(computerUsePreview: ComputerTool) = addTool(Tool.ofComputerUsePreview(computerUsePreview)) - /** Alias for calling [addTool] with `Tool.ofWebSearch(webSearch)`. */ - fun addTool(webSearch: WebSearchTool) = addTool(Tool.ofWebSearch(webSearch)) - /** * An alternative to sampling with temperature, called nucleus sampling, where the model * considers the results of the tokens with top_p probability mass. So 0.1 means only diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt index b85d679a..ea8d203f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt @@ -12,6 +12,10 @@ import com.openai.errors.OpenAIInvalidDataException * - `file_search_call.results`: Include the search results of the file search tool call. * - `message.input_image.image_url`: Include image urls from the input message. * - `computer_call_output.output.image_url`: Include image urls from the computer call output. + * - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning + * item outputs. This enables reasoning items to be used in multi-turn conversations when using + * the Responses API statelessly (like when the `store` parameter is set to `false`, or when an + * organization is enrolled in the zero data retention program). */ class ResponseIncludable @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -34,6 +38,8 @@ class ResponseIncludable @JsonCreator private constructor(private val value: Jso @JvmField val COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL = of("computer_call_output.output.image_url") + @JvmField val REASONING_ENCRYPTED_CONTENT = of("reasoning.encrypted_content") + @JvmStatic fun of(value: String) = ResponseIncludable(JsonField.of(value)) } @@ -42,6 +48,7 @@ class ResponseIncludable @JsonCreator private constructor(private val value: Jso FILE_SEARCH_CALL_RESULTS, MESSAGE_INPUT_IMAGE_IMAGE_URL, COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL, + REASONING_ENCRYPTED_CONTENT, } /** @@ -57,6 +64,7 @@ class ResponseIncludable @JsonCreator private constructor(private val value: Jso FILE_SEARCH_CALL_RESULTS, MESSAGE_INPUT_IMAGE_IMAGE_URL, COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL, + REASONING_ENCRYPTED_CONTENT, /** * An enum member indicating that [ResponseIncludable] was instantiated with an unknown * value. @@ -76,6 +84,7 @@ class ResponseIncludable @JsonCreator private constructor(private val value: Jso FILE_SEARCH_CALL_RESULTS -> Value.FILE_SEARCH_CALL_RESULTS MESSAGE_INPUT_IMAGE_IMAGE_URL -> Value.MESSAGE_INPUT_IMAGE_IMAGE_URL COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL -> Value.COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL + REASONING_ENCRYPTED_CONTENT -> Value.REASONING_ENCRYPTED_CONTENT else -> Value._UNKNOWN } @@ -92,6 +101,7 @@ class ResponseIncludable @JsonCreator private constructor(private val value: Jso FILE_SEARCH_CALL_RESULTS -> Known.FILE_SEARCH_CALL_RESULTS MESSAGE_INPUT_IMAGE_IMAGE_URL -> Known.MESSAGE_INPUT_IMAGE_IMAGE_URL COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL -> Known.COMPUTER_CALL_OUTPUT_OUTPUT_IMAGE_URL + REASONING_ENCRYPTED_CONTENT -> Known.REASONING_ENCRYPTED_CONTENT else -> throw OpenAIInvalidDataException("Unknown ResponseIncludable: $value") } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt index 48f5f284..d2ba96f6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt @@ -14,6 +14,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** A file input to the model. */ class ResponseInputFile @@ -153,7 +154,10 @@ private constructor( fun fileData(fileData: JsonField) = apply { this.fileData = fileData } /** The ID of the file to be sent to the model. */ - fun fileId(fileId: String) = fileId(JsonField.of(fileId)) + fun fileId(fileId: String?) = fileId(JsonField.ofNullable(fileId)) + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) /** * Sets [Builder.fileId] to an arbitrary JSON value. diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt index ad4dbd7b..a258a7ba 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt @@ -298,10 +298,10 @@ private constructor( companion object { - @JvmField val HIGH = of("high") - @JvmField val LOW = of("low") + @JvmField val HIGH = of("high") + @JvmField val AUTO = of("auto") @JvmStatic fun of(value: String) = Detail(JsonField.of(value)) @@ -309,8 +309,8 @@ private constructor( /** An enum containing [Detail]'s known values. */ enum class Known { - HIGH, LOW, + HIGH, AUTO, } @@ -324,8 +324,8 @@ private constructor( * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - HIGH, LOW, + HIGH, AUTO, /** An enum member indicating that [Detail] was instantiated with an unknown value. */ _UNKNOWN, @@ -340,8 +340,8 @@ private constructor( */ fun value(): Value = when (this) { - HIGH -> Value.HIGH LOW -> Value.LOW + HIGH -> Value.HIGH AUTO -> Value.AUTO else -> Value._UNKNOWN } @@ -357,8 +357,8 @@ private constructor( */ fun known(): Known = when (this) { - HIGH -> Known.HIGH LOW -> Known.LOW + HIGH -> Known.HIGH AUTO -> Known.AUTO else -> throw OpenAIInvalidDataException("Unknown Detail: $value") } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt index 9740c728..121ff3bc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt @@ -110,6 +110,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a response. + * Be sure to include these items in your `input` to the Responses API for subsequent turns of a + * conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ fun reasoning(): Optional = Optional.ofNullable(reasoning) @@ -195,6 +198,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a response. + * Be sure to include these items in your `input` to the Responses API for subsequent turns of a + * conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ fun asReasoning(): ResponseReasoningItem = reasoning.getOrThrow("reasoning") @@ -434,7 +440,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a - * response. + * response. Be sure to include these items in your `input` to the Responses API for + * subsequent turns of a conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ @JvmStatic fun ofReasoning(reasoning: ResponseReasoningItem) = ResponseInputItem(reasoning = reasoning) @@ -505,7 +513,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a - * response. + * response. Be sure to include these items in your `input` to the Responses API for + * subsequent turns of a conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ fun visitReasoning(reasoning: ResponseReasoningItem): T @@ -1575,7 +1585,10 @@ private constructor( fun type(type: JsonValue) = apply { this.type = type } /** The ID of the computer tool call output. */ - fun id(id: String) = id(JsonField.of(id)) + fun id(id: String?) = id(JsonField.ofNullable(id)) + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) /** * Sets [Builder.id] to an arbitrary JSON value. @@ -1589,8 +1602,16 @@ private constructor( /** * The safety checks reported by the API that have been acknowledged by the developer. */ - fun acknowledgedSafetyChecks(acknowledgedSafetyChecks: List) = - acknowledgedSafetyChecks(JsonField.of(acknowledgedSafetyChecks)) + fun acknowledgedSafetyChecks(acknowledgedSafetyChecks: List?) = + acknowledgedSafetyChecks(JsonField.ofNullable(acknowledgedSafetyChecks)) + + /** + * Alias for calling [Builder.acknowledgedSafetyChecks] with + * `acknowledgedSafetyChecks.orElse(null)`. + */ + fun acknowledgedSafetyChecks( + acknowledgedSafetyChecks: Optional> + ) = acknowledgedSafetyChecks(acknowledgedSafetyChecks.getOrNull()) /** * Sets [Builder.acknowledgedSafetyChecks] to an arbitrary JSON value. @@ -1622,7 +1643,10 @@ private constructor( * The status of the message input. One of `in_progress`, `completed`, or `incomplete`. * Populated when input items are returned via API. */ - fun status(status: Status) = status(JsonField.of(status)) + fun status(status: Status?) = status(JsonField.ofNullable(status)) + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) /** * Sets [Builder.status] to an arbitrary JSON value. @@ -1751,20 +1775,18 @@ private constructor( /** * The type of the pending safety check. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun code(): String = code.getRequired("code") + fun code(): Optional = code.getOptional("code") /** * Details about the pending safety check. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun message(): String = message.getRequired("message") + fun message(): Optional = message.getOptional("message") /** * Returns the raw JSON value of [id]. @@ -1808,8 +1830,6 @@ private constructor( * The following fields are required: * ```java * .id() - * .code() - * .message() * ``` */ @JvmStatic fun builder() = Builder() @@ -1819,8 +1839,8 @@ private constructor( class Builder internal constructor() { private var id: JsonField? = null - private var code: JsonField? = null - private var message: JsonField? = null + private var code: JsonField = JsonMissing.of() + private var message: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -1845,7 +1865,10 @@ private constructor( fun id(id: JsonField) = apply { this.id = id } /** The type of the pending safety check. */ - fun code(code: String) = code(JsonField.of(code)) + fun code(code: String?) = code(JsonField.ofNullable(code)) + + /** Alias for calling [Builder.code] with `code.orElse(null)`. */ + fun code(code: Optional) = code(code.getOrNull()) /** * Sets [Builder.code] to an arbitrary JSON value. @@ -1857,7 +1880,10 @@ private constructor( fun code(code: JsonField) = apply { this.code = code } /** Details about the pending safety check. */ - fun message(message: String) = message(JsonField.of(message)) + fun message(message: String?) = message(JsonField.ofNullable(message)) + + /** Alias for calling [Builder.message] with `message.orElse(null)`. */ + fun message(message: Optional) = message(message.getOrNull()) /** * Sets [Builder.message] to an arbitrary JSON value. @@ -1898,8 +1924,6 @@ private constructor( * The following fields are required: * ```java * .id() - * .code() - * .message() * ``` * * @throws IllegalStateException if any required field is unset. @@ -1907,8 +1931,8 @@ private constructor( fun build(): AcknowledgedSafetyCheck = AcknowledgedSafetyCheck( checkRequired("id", id), - checkRequired("code", code), - checkRequired("message", message), + code, + message, additionalProperties.toMutableMap(), ) } @@ -2304,7 +2328,10 @@ private constructor( * The unique ID of the function tool call output. Populated when this item is returned * via API. */ - fun id(id: String) = id(JsonField.of(id)) + fun id(id: String?) = id(JsonField.ofNullable(id)) + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) /** * Sets [Builder.id] to an arbitrary JSON value. @@ -2319,7 +2346,10 @@ private constructor( * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated * when items are returned via API. */ - fun status(status: Status) = status(JsonField.of(status)) + fun status(status: Status?) = status(JsonField.ofNullable(status)) + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) /** * Sets [Builder.status] to an arbitrary JSON value. @@ -2575,14 +2605,14 @@ private constructor( class ItemReference private constructor( private val id: JsonField, - private val type: JsonValue, + private val type: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), ) : this(id, type, mutableMapOf()) /** @@ -2596,15 +2626,10 @@ private constructor( /** * The type of item to reference. Always `item_reference`. * - * Expected to always return the following: - * ```java - * JsonValue.from("item_reference") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + fun type(): Optional = type.getOptional("type") /** * Returns the raw JSON value of [id]. @@ -2613,6 +2638,13 @@ private constructor( */ @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -2642,7 +2674,7 @@ private constructor( class Builder internal constructor() { private var id: JsonField? = null - private var type: JsonValue = JsonValue.from("item_reference") + private var type: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -2664,19 +2696,20 @@ private constructor( */ fun id(id: JsonField) = apply { this.id = id } + /** The type of item to reference. Always `item_reference`. */ + fun type(type: Type?) = type(JsonField.ofNullable(type)) + + /** Alias for calling [Builder.type] with `type.orElse(null)`. */ + fun type(type: Optional) = type(type.getOrNull()) + /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("item_reference") - * ``` + * Sets [Builder.type] to an arbitrary JSON value. * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun type(type: JsonValue) = apply { this.type = type } + fun type(type: JsonField) = apply { this.type = type } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -2721,11 +2754,7 @@ private constructor( } id() - _type().let { - if (it != JsonValue.from("item_reference")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } + type().ifPresent { it.validate() } validated = true } @@ -2745,8 +2774,129 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (id.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("item_reference")) 1 else 0 } + (if (id.asKnown().isPresent) 1 else 0) + (type.asKnown().getOrNull()?.validity() ?: 0) + + /** The type of item to reference. Always `item_reference`. */ + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val ITEM_REFERENCE = of("item_reference") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + ITEM_REFERENCE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ITEM_REFERENCE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ITEM_REFERENCE -> Value.ITEM_REFERENCE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ITEM_REFERENCE -> Known.ITEM_REFERENCE + else -> throw OpenAIInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Type && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } override fun equals(other: Any?): Boolean { if (this === other) { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt index ec08f805..9d7a3acc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt @@ -65,6 +65,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a response. + * Be sure to include these items in your `input` to the Responses API for subsequent turns of a + * conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ fun reasoning(): Optional = Optional.ofNullable(reasoning) @@ -113,6 +116,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a response. + * Be sure to include these items in your `input` to the Responses API for subsequent turns of a + * conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ fun asReasoning(): ResponseReasoningItem = reasoning.getOrThrow("reasoning") @@ -269,7 +275,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a - * response. + * response. Be sure to include these items in your `input` to the Responses API for + * subsequent turns of a conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ @JvmStatic fun ofReasoning(reasoning: ResponseReasoningItem) = @@ -315,7 +323,9 @@ private constructor( /** * A description of the chain of thought used by a reasoning model while generating a - * response. + * response. Be sure to include these items in your `input` to the Responses API for + * subsequent turns of a conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). */ fun visitReasoning(reasoning: ResponseReasoningItem): T diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt index eb6db855..166b2f7d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt @@ -20,12 +20,18 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** A description of the chain of thought used by a reasoning model while generating a response. */ +/** + * A description of the chain of thought used by a reasoning model while generating a response. Be + * sure to include these items in your `input` to the Responses API for subsequent turns of a + * conversation if you are manually + * [managing context](https://platform.openai.com/docs/guides/conversation-state). + */ class ResponseReasoningItem private constructor( private val id: JsonField, private val summary: JsonField>, private val type: JsonValue, + private val encryptedContent: JsonField, private val status: JsonField, private val additionalProperties: MutableMap, ) { @@ -37,8 +43,11 @@ private constructor( @ExcludeMissing summary: JsonField> = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("encrypted_content") + @ExcludeMissing + encryptedContent: JsonField = JsonMissing.of(), @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), - ) : this(id, summary, type, status, mutableMapOf()) + ) : this(id, summary, type, encryptedContent, status, mutableMapOf()) /** * The unique identifier of the reasoning content. @@ -69,6 +78,15 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + /** + * The encrypted content of the reasoning item - populated when a response is generated with + * `reasoning.encrypted_content` in the `include` parameter. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun encryptedContent(): Optional = encryptedContent.getOptional("encrypted_content") + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when * items are returned via API. @@ -92,6 +110,16 @@ private constructor( */ @JsonProperty("summary") @ExcludeMissing fun _summary(): JsonField> = summary + /** + * Returns the raw JSON value of [encryptedContent]. + * + * Unlike [encryptedContent], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("encrypted_content") + @ExcludeMissing + fun _encryptedContent(): JsonField = encryptedContent + /** * Returns the raw JSON value of [status]. * @@ -131,6 +159,7 @@ private constructor( private var id: JsonField? = null private var summary: JsonField>? = null private var type: JsonValue = JsonValue.from("reasoning") + private var encryptedContent: JsonField = JsonMissing.of() private var status: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -139,6 +168,7 @@ private constructor( id = responseReasoningItem.id summary = responseReasoningItem.summary.map { it.toMutableList() } type = responseReasoningItem.type + encryptedContent = responseReasoningItem.encryptedContent status = responseReasoningItem.status additionalProperties = responseReasoningItem.additionalProperties.toMutableMap() } @@ -194,6 +224,28 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } + /** + * The encrypted content of the reasoning item - populated when a response is generated with + * `reasoning.encrypted_content` in the `include` parameter. + */ + fun encryptedContent(encryptedContent: String?) = + encryptedContent(JsonField.ofNullable(encryptedContent)) + + /** Alias for calling [Builder.encryptedContent] with `encryptedContent.orElse(null)`. */ + fun encryptedContent(encryptedContent: Optional) = + encryptedContent(encryptedContent.getOrNull()) + + /** + * Sets [Builder.encryptedContent] to an arbitrary JSON value. + * + * You should usually call [Builder.encryptedContent] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun encryptedContent(encryptedContent: JsonField) = apply { + this.encryptedContent = encryptedContent + } + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated * when items are returned via API. @@ -245,6 +297,7 @@ private constructor( checkRequired("id", id), checkRequired("summary", summary).map { it.toImmutable() }, type, + encryptedContent, status, additionalProperties.toMutableMap(), ) @@ -264,6 +317,7 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } + encryptedContent() status().ifPresent { it.validate() } validated = true } @@ -286,6 +340,7 @@ private constructor( (if (id.asKnown().isPresent) 1 else 0) + (summary.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + type.let { if (it == JsonValue.from("reasoning")) 1 else 0 } + + (if (encryptedContent.asKnown().isPresent) 1 else 0) + (status.asKnown().getOrNull()?.validity() ?: 0) class Summary @@ -622,15 +677,15 @@ private constructor( return true } - return /* spotless:off */ other is ResponseReasoningItem && id == other.id && summary == other.summary && type == other.type && status == other.status && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is ResponseReasoningItem && id == other.id && summary == other.summary && type == other.type && encryptedContent == other.encryptedContent && status == other.status && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(id, summary, type, status, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(id, summary, type, encryptedContent, status, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "ResponseReasoningItem{id=$id, summary=$summary, type=$type, status=$status, additionalProperties=$additionalProperties}" + "ResponseReasoningItem{id=$id, summary=$summary, type=$type, encryptedContent=$encryptedContent, status=$status, additionalProperties=$additionalProperties}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt index 26b0d530..e0fe71bc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt @@ -18,18 +18,15 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** - * A tool that searches for relevant content from uploaded files. Learn more about the - * [file search tool](https://platform.openai.com/docs/guides/tools-file-search). - */ +/** A tool that can be used to generate a response. */ @JsonDeserialize(using = Tool.Deserializer::class) @JsonSerialize(using = Tool.Serializer::class) class Tool private constructor( private val fileSearch: FileSearchTool? = null, private val function: FunctionTool? = null, - private val computerUsePreview: ComputerTool? = null, private val webSearch: WebSearchTool? = null, + private val computerUsePreview: ComputerTool? = null, private val _json: JsonValue? = null, ) { @@ -45,26 +42,26 @@ private constructor( */ fun function(): Optional = Optional.ofNullable(function) - /** - * A tool that controls a virtual computer. Learn more about the - * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). - */ - fun computerUsePreview(): Optional = Optional.ofNullable(computerUsePreview) - /** * This tool searches the web for relevant results to use in a response. Learn more about the * [web search tool](https://platform.openai.com/docs/guides/tools-web-search). */ fun webSearch(): Optional = Optional.ofNullable(webSearch) + /** + * A tool that controls a virtual computer. Learn more about the + * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + */ + fun computerUsePreview(): Optional = Optional.ofNullable(computerUsePreview) + fun isFileSearch(): Boolean = fileSearch != null fun isFunction(): Boolean = function != null - fun isComputerUsePreview(): Boolean = computerUsePreview != null - fun isWebSearch(): Boolean = webSearch != null + fun isComputerUsePreview(): Boolean = computerUsePreview != null + /** * A tool that searches for relevant content from uploaded files. Learn more about the * [file search tool](https://platform.openai.com/docs/guides/tools-file-search). @@ -77,26 +74,26 @@ private constructor( */ fun asFunction(): FunctionTool = function.getOrThrow("function") - /** - * A tool that controls a virtual computer. Learn more about the - * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). - */ - fun asComputerUsePreview(): ComputerTool = computerUsePreview.getOrThrow("computerUsePreview") - /** * This tool searches the web for relevant results to use in a response. Learn more about the * [web search tool](https://platform.openai.com/docs/guides/tools-web-search). */ fun asWebSearch(): WebSearchTool = webSearch.getOrThrow("webSearch") + /** + * A tool that controls a virtual computer. Learn more about the + * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + */ + fun asComputerUsePreview(): ComputerTool = computerUsePreview.getOrThrow("computerUsePreview") + fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = when { fileSearch != null -> visitor.visitFileSearch(fileSearch) function != null -> visitor.visitFunction(function) - computerUsePreview != null -> visitor.visitComputerUsePreview(computerUsePreview) webSearch != null -> visitor.visitWebSearch(webSearch) + computerUsePreview != null -> visitor.visitComputerUsePreview(computerUsePreview) else -> visitor.unknown(_json) } @@ -117,13 +114,13 @@ private constructor( function.validate() } - override fun visitComputerUsePreview(computerUsePreview: ComputerTool) { - computerUsePreview.validate() - } - override fun visitWebSearch(webSearch: WebSearchTool) { webSearch.validate() } + + override fun visitComputerUsePreview(computerUsePreview: ComputerTool) { + computerUsePreview.validate() + } } ) validated = true @@ -150,11 +147,11 @@ private constructor( override fun visitFunction(function: FunctionTool) = function.validity() + override fun visitWebSearch(webSearch: WebSearchTool) = webSearch.validity() + override fun visitComputerUsePreview(computerUsePreview: ComputerTool) = computerUsePreview.validity() - override fun visitWebSearch(webSearch: WebSearchTool) = webSearch.validity() - override fun unknown(json: JsonValue?) = 0 } ) @@ -164,17 +161,17 @@ private constructor( return true } - return /* spotless:off */ other is Tool && fileSearch == other.fileSearch && function == other.function && computerUsePreview == other.computerUsePreview && webSearch == other.webSearch /* spotless:on */ + return /* spotless:off */ other is Tool && fileSearch == other.fileSearch && function == other.function && webSearch == other.webSearch && computerUsePreview == other.computerUsePreview /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(fileSearch, function, computerUsePreview, webSearch) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(fileSearch, function, webSearch, computerUsePreview) /* spotless:on */ override fun toString(): String = when { fileSearch != null -> "Tool{fileSearch=$fileSearch}" function != null -> "Tool{function=$function}" - computerUsePreview != null -> "Tool{computerUsePreview=$computerUsePreview}" webSearch != null -> "Tool{webSearch=$webSearch}" + computerUsePreview != null -> "Tool{computerUsePreview=$computerUsePreview}" _json != null -> "Tool{_unknown=$_json}" else -> throw IllegalStateException("Invalid Tool") } @@ -193,6 +190,12 @@ private constructor( */ @JvmStatic fun ofFunction(function: FunctionTool) = Tool(function = function) + /** + * This tool searches the web for relevant results to use in a response. Learn more about + * the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + */ + @JvmStatic fun ofWebSearch(webSearch: WebSearchTool) = Tool(webSearch = webSearch) + /** * A tool that controls a virtual computer. Learn more about the * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). @@ -200,12 +203,6 @@ private constructor( @JvmStatic fun ofComputerUsePreview(computerUsePreview: ComputerTool) = Tool(computerUsePreview = computerUsePreview) - - /** - * This tool searches the web for relevant results to use in a response. Learn more about - * the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). - */ - @JvmStatic fun ofWebSearch(webSearch: WebSearchTool) = Tool(webSearch = webSearch) } /** An interface that defines how to map each variant of [Tool] to a value of type [T]. */ @@ -223,18 +220,18 @@ private constructor( */ fun visitFunction(function: FunctionTool): T - /** - * A tool that controls a virtual computer. Learn more about the - * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). - */ - fun visitComputerUsePreview(computerUsePreview: ComputerTool): T - /** * This tool searches the web for relevant results to use in a response. Learn more about * the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). */ fun visitWebSearch(webSearch: WebSearchTool): T + /** + * A tool that controls a virtual computer. Learn more about the + * [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + */ + fun visitComputerUsePreview(computerUsePreview: ComputerTool): T + /** * Maps an unknown variant of [Tool] to a value of type [T]. * @@ -289,8 +286,8 @@ private constructor( when { value.fileSearch != null -> generator.writeObject(value.fileSearch) value.function != null -> generator.writeObject(value.function) - value.computerUsePreview != null -> generator.writeObject(value.computerUsePreview) value.webSearch != null -> generator.writeObject(value.webSearch) + value.computerUsePreview != null -> generator.writeObject(value.computerUsePreview) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Tool") } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt index ac1f06c1..82225e99 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt @@ -42,9 +42,8 @@ private constructor( ) : this(type, searchContextSize, userLocation, mutableMapOf()) /** - * The type of the web search tool. One of: - * - `web_search_preview` - * - `web_search_preview_2025_03_11` + * The type of the web search tool. One of `web_search_preview` or + * `web_search_preview_2025_03_11`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -62,6 +61,8 @@ private constructor( searchContextSize.getOptional("search_context_size") /** + * The user's location. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ @@ -135,9 +136,8 @@ private constructor( } /** - * The type of the web search tool. One of: - * - `web_search_preview` - * - `web_search_preview_2025_03_11` + * The type of the web search tool. One of `web_search_preview` or + * `web_search_preview_2025_03_11`. */ fun type(type: Type) = type(JsonField.of(type)) @@ -167,6 +167,7 @@ private constructor( this.searchContextSize = searchContextSize } + /** The user's location. */ fun userLocation(userLocation: UserLocation?) = userLocation(JsonField.ofNullable(userLocation)) @@ -258,9 +259,8 @@ private constructor( (userLocation.asKnown().getOrNull()?.validity() ?: 0) /** - * The type of the web search tool. One of: - * - `web_search_preview` - * - `web_search_preview_2025_03_11` + * The type of the web search tool. One of `web_search_preview` or + * `web_search_preview_2025_03_11`. */ class Type @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -526,6 +526,7 @@ private constructor( override fun toString() = value.toString() } + /** The user's location. */ class UserLocation private constructor( private val type: JsonValue, @@ -673,7 +674,10 @@ private constructor( fun type(type: JsonValue) = apply { this.type = type } /** Free text input for the city of the user, e.g. `San Francisco`. */ - fun city(city: String) = city(JsonField.of(city)) + fun city(city: String?) = city(JsonField.ofNullable(city)) + + /** Alias for calling [Builder.city] with `city.orElse(null)`. */ + fun city(city: Optional) = city(city.getOrNull()) /** * Sets [Builder.city] to an arbitrary JSON value. @@ -688,7 +692,10 @@ private constructor( * The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the * user, e.g. `US`. */ - fun country(country: String) = country(JsonField.of(country)) + fun country(country: String?) = country(JsonField.ofNullable(country)) + + /** Alias for calling [Builder.country] with `country.orElse(null)`. */ + fun country(country: Optional) = country(country.getOrNull()) /** * Sets [Builder.country] to an arbitrary JSON value. @@ -700,7 +707,10 @@ private constructor( fun country(country: JsonField) = apply { this.country = country } /** Free text input for the region of the user, e.g. `California`. */ - fun region(region: String) = region(JsonField.of(region)) + fun region(region: String?) = region(JsonField.ofNullable(region)) + + /** Alias for calling [Builder.region] with `region.orElse(null)`. */ + fun region(region: Optional) = region(region.getOrNull()) /** * Sets [Builder.region] to an arbitrary JSON value. @@ -715,7 +725,10 @@ private constructor( * The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, * e.g. `America/Los_Angeles`. */ - fun timezone(timezone: String) = timezone(JsonField.of(timezone)) + fun timezone(timezone: String?) = timezone(JsonField.ofNullable(timezone)) + + /** Alias for calling [Builder.timezone] with `timezone.orElse(null)`. */ + fun timezone(timezone: Optional) = timezone(timezone.getOrNull()) /** * Sets [Builder.timezone] to an arbitrary JSON value. diff --git a/openai-java-core/src/test/kotlin/com/openai/models/images/ImageEditParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/images/ImageEditParamsTest.kt index d89fb59a..0111ca41 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/images/ImageEditParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/images/ImageEditParamsTest.kt @@ -14,6 +14,7 @@ internal class ImageEditParamsTest { ImageEditParams.builder() .image("some content".byteInputStream()) .prompt("A cute baby sea otter wearing a beret") + .background(ImageEditParams.Background.TRANSPARENT) .mask("some content".byteInputStream()) .model(ImageModel.DALL_E_2) .n(1L) @@ -30,6 +31,7 @@ internal class ImageEditParamsTest { ImageEditParams.builder() .image("some content".byteInputStream()) .prompt("A cute baby sea otter wearing a beret") + .background(ImageEditParams.Background.TRANSPARENT) .mask("some content".byteInputStream()) .model(ImageModel.DALL_E_2) .n(1L) @@ -58,6 +60,7 @@ internal class ImageEditParamsTest { ) ), "prompt" to MultipartField.of("A cute baby sea otter wearing a beret"), + "background" to MultipartField.of(ImageEditParams.Background.TRANSPARENT), "mask" to MultipartField.of("some content".byteInputStream()), "model" to MultipartField.of(ImageModel.DALL_E_2), "n" to MultipartField.of(1L), diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt index be2fdaa6..42640334 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt @@ -13,14 +13,14 @@ internal class ComputerToolTest { fun create() { val computerTool = ComputerTool.builder() - .displayHeight(0.0) - .displayWidth(0.0) - .environment(ComputerTool.Environment.MAC) + .displayHeight(0L) + .displayWidth(0L) + .environment(ComputerTool.Environment.WINDOWS) .build() - assertThat(computerTool.displayHeight()).isEqualTo(0.0) - assertThat(computerTool.displayWidth()).isEqualTo(0.0) - assertThat(computerTool.environment()).isEqualTo(ComputerTool.Environment.MAC) + assertThat(computerTool.displayHeight()).isEqualTo(0L) + assertThat(computerTool.displayWidth()).isEqualTo(0L) + assertThat(computerTool.environment()).isEqualTo(ComputerTool.Environment.WINDOWS) } @Test @@ -28,9 +28,9 @@ internal class ComputerToolTest { val jsonMapper = jsonMapper() val computerTool = ComputerTool.builder() - .displayHeight(0.0) - .displayWidth(0.0) - .environment(ComputerTool.Environment.MAC) + .displayHeight(0L) + .displayWidth(0L) + .environment(ComputerTool.Environment.WINDOWS) .build() val roundtrippedComputerTool = diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt index 0769309b..861ad293 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt @@ -26,12 +26,12 @@ internal class FunctionToolTest { assertThat(functionTool.name()).isEqualTo("name") assertThat(functionTool.parameters()) - .isEqualTo( + .contains( FunctionTool.Parameters.builder() .putAdditionalProperty("foo", JsonValue.from("bar")) .build() ) - assertThat(functionTool.strict()).isEqualTo(true) + assertThat(functionTool.strict()).contains(true) assertThat(functionTool.description()).contains("description") } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt index 062f34e7..e8792529 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt @@ -46,7 +46,7 @@ internal class ResponseContentTest { fun ofInputImage() { val inputImage = ResponseInputImage.builder() - .detail(ResponseInputImage.Detail.HIGH) + .detail(ResponseInputImage.Detail.LOW) .fileId("file_id") .imageUrl("image_url") .build() @@ -66,7 +66,7 @@ internal class ResponseContentTest { val responseContent = ResponseContent.ofInputImage( ResponseInputImage.builder() - .detail(ResponseInputImage.Detail.HIGH) + .detail(ResponseInputImage.Detail.LOW) .fileId("file_id") .imageUrl("image_url") .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt index b28c4c28..c5893194 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt @@ -44,7 +44,7 @@ internal class ResponseInputContentTest { fun ofInputImage() { val inputImage = ResponseInputImage.builder() - .detail(ResponseInputImage.Detail.HIGH) + .detail(ResponseInputImage.Detail.LOW) .fileId("file_id") .imageUrl("image_url") .build() @@ -62,7 +62,7 @@ internal class ResponseInputContentTest { val responseInputContent = ResponseInputContent.ofInputImage( ResponseInputImage.builder() - .detail(ResponseInputImage.Detail.HIGH) + .detail(ResponseInputImage.Detail.LOW) .fileId("file_id") .imageUrl("image_url") .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt index b6ae8c26..8d753c1a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt @@ -13,12 +13,12 @@ internal class ResponseInputImageTest { fun create() { val responseInputImage = ResponseInputImage.builder() - .detail(ResponseInputImage.Detail.HIGH) + .detail(ResponseInputImage.Detail.LOW) .fileId("file_id") .imageUrl("image_url") .build() - assertThat(responseInputImage.detail()).isEqualTo(ResponseInputImage.Detail.HIGH) + assertThat(responseInputImage.detail()).isEqualTo(ResponseInputImage.Detail.LOW) assertThat(responseInputImage.fileId()).contains("file_id") assertThat(responseInputImage.imageUrl()).contains("image_url") } @@ -28,7 +28,7 @@ internal class ResponseInputImageTest { val jsonMapper = jsonMapper() val responseInputImage = ResponseInputImage.builder() - .detail(ResponseInputImage.Detail.HIGH) + .detail(ResponseInputImage.Detail.LOW) .fileId("file_id") .imageUrl("image_url") .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt index 820cb215..4ca8296c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt @@ -321,7 +321,7 @@ internal class ResponseInputItemTest { fun ofComputerCallOutput() { val computerCallOutput = ResponseInputItem.ComputerCallOutput.builder() - .callId("call_id") + .callId("x") .output( ResponseComputerToolCallOutputScreenshot.builder() .fileId("file_id") @@ -360,7 +360,7 @@ internal class ResponseInputItemTest { val responseInputItem = ResponseInputItem.ofComputerCallOutput( ResponseInputItem.ComputerCallOutput.builder() - .callId("call_id") + .callId("x") .output( ResponseComputerToolCallOutputScreenshot.builder() .fileId("file_id") @@ -484,7 +484,7 @@ internal class ResponseInputItemTest { fun ofFunctionCallOutput() { val functionCallOutput = ResponseInputItem.FunctionCallOutput.builder() - .callId("call_id") + .callId("x") .output("output") .id("id") .status(ResponseInputItem.FunctionCallOutput.Status.IN_PROGRESS) @@ -511,7 +511,7 @@ internal class ResponseInputItemTest { val responseInputItem = ResponseInputItem.ofFunctionCallOutput( ResponseInputItem.FunctionCallOutput.builder() - .callId("call_id") + .callId("x") .output("output") .id("id") .status(ResponseInputItem.FunctionCallOutput.Status.IN_PROGRESS) @@ -533,6 +533,7 @@ internal class ResponseInputItemTest { ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .encryptedContent("encrypted_content") .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() @@ -559,6 +560,7 @@ internal class ResponseInputItemTest { ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .encryptedContent("encrypted_content") .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() ) @@ -574,7 +576,11 @@ internal class ResponseInputItemTest { @Test fun ofItemReference() { - val itemReference = ResponseInputItem.ItemReference.builder().id("id").build() + val itemReference = + ResponseInputItem.ItemReference.builder() + .id("id") + .type(ResponseInputItem.ItemReference.Type.ITEM_REFERENCE) + .build() val responseInputItem = ResponseInputItem.ofItemReference(itemReference) @@ -596,7 +602,10 @@ internal class ResponseInputItemTest { val jsonMapper = jsonMapper() val responseInputItem = ResponseInputItem.ofItemReference( - ResponseInputItem.ItemReference.builder().id("id").build() + ResponseInputItem.ItemReference.builder() + .id("id") + .type(ResponseInputItem.ItemReference.Type.ITEM_REFERENCE) + .build() ) val roundtrippedResponseInputItem = diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt index 25394226..763f097a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt @@ -298,6 +298,7 @@ internal class ResponseOutputItemTest { ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .encryptedContent("encrypted_content") .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() @@ -319,6 +320,7 @@ internal class ResponseOutputItemTest { ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .encryptedContent("encrypted_content") .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt index 1cfdfeef..37c9ee47 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt @@ -15,12 +15,14 @@ internal class ResponseReasoningItemTest { ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .encryptedContent("encrypted_content") .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() assertThat(responseReasoningItem.id()).isEqualTo("id") assertThat(responseReasoningItem.summary()) .containsExactly(ResponseReasoningItem.Summary.builder().text("text").build()) + assertThat(responseReasoningItem.encryptedContent()).contains("encrypted_content") assertThat(responseReasoningItem.status()) .contains(ResponseReasoningItem.Status.IN_PROGRESS) } @@ -32,6 +34,7 @@ internal class ResponseReasoningItemTest { ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .encryptedContent("encrypted_content") .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt index 694bdf15..e5f6c33f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt @@ -40,8 +40,8 @@ internal class ToolTest { assertThat(tool.fileSearch()).contains(fileSearch) assertThat(tool.function()).isEmpty - assertThat(tool.computerUsePreview()).isEmpty assertThat(tool.webSearch()).isEmpty + assertThat(tool.computerUsePreview()).isEmpty } @Test @@ -92,8 +92,8 @@ internal class ToolTest { assertThat(tool.fileSearch()).isEmpty assertThat(tool.function()).contains(function) - assertThat(tool.computerUsePreview()).isEmpty assertThat(tool.webSearch()).isEmpty + assertThat(tool.computerUsePreview()).isEmpty } @Test @@ -119,41 +119,6 @@ internal class ToolTest { assertThat(roundtrippedTool).isEqualTo(tool) } - @Test - fun ofComputerUsePreview() { - val computerUsePreview = - ComputerTool.builder() - .displayHeight(0.0) - .displayWidth(0.0) - .environment(ComputerTool.Environment.MAC) - .build() - - val tool = Tool.ofComputerUsePreview(computerUsePreview) - - assertThat(tool.fileSearch()).isEmpty - assertThat(tool.function()).isEmpty - assertThat(tool.computerUsePreview()).contains(computerUsePreview) - assertThat(tool.webSearch()).isEmpty - } - - @Test - fun ofComputerUsePreviewRoundtrip() { - val jsonMapper = jsonMapper() - val tool = - Tool.ofComputerUsePreview( - ComputerTool.builder() - .displayHeight(0.0) - .displayWidth(0.0) - .environment(ComputerTool.Environment.MAC) - .build() - ) - - val roundtrippedTool = - jsonMapper.readValue(jsonMapper.writeValueAsString(tool), jacksonTypeRef()) - - assertThat(roundtrippedTool).isEqualTo(tool) - } - @Test fun ofWebSearch() { val webSearch = @@ -174,8 +139,8 @@ internal class ToolTest { assertThat(tool.fileSearch()).isEmpty assertThat(tool.function()).isEmpty - assertThat(tool.computerUsePreview()).isEmpty assertThat(tool.webSearch()).contains(webSearch) + assertThat(tool.computerUsePreview()).isEmpty } @Test @@ -203,6 +168,41 @@ internal class ToolTest { assertThat(roundtrippedTool).isEqualTo(tool) } + @Test + fun ofComputerUsePreview() { + val computerUsePreview = + ComputerTool.builder() + .displayHeight(0L) + .displayWidth(0L) + .environment(ComputerTool.Environment.WINDOWS) + .build() + + val tool = Tool.ofComputerUsePreview(computerUsePreview) + + assertThat(tool.fileSearch()).isEmpty + assertThat(tool.function()).isEmpty + assertThat(tool.webSearch()).isEmpty + assertThat(tool.computerUsePreview()).contains(computerUsePreview) + } + + @Test + fun ofComputerUsePreviewRoundtrip() { + val jsonMapper = jsonMapper() + val tool = + Tool.ofComputerUsePreview( + ComputerTool.builder() + .displayHeight(0L) + .displayWidth(0L) + .environment(ComputerTool.Environment.WINDOWS) + .build() + ) + + val roundtrippedTool = + jsonMapper.readValue(jsonMapper.writeValueAsString(tool), jacksonTypeRef()) + + assertThat(roundtrippedTool).isEqualTo(tool) + } + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { BOOLEAN(JsonValue.from(false)), STRING(JsonValue.from("invalid")), diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt index 6b059d17..f2f7ff0c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt @@ -53,6 +53,7 @@ internal class ImageServiceAsyncTest { ImageEditParams.builder() .image("some content".byteInputStream()) .prompt("A cute baby sea otter wearing a beret") + .background(ImageEditParams.Background.TRANSPARENT) .mask("some content".byteInputStream()) .model(ImageModel.DALL_E_2) .n(1L) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt index 6de463c9..6d3c7507 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt @@ -52,6 +52,7 @@ internal class ImageServiceTest { ImageEditParams.builder() .image("some content".byteInputStream()) .prompt("A cute baby sea otter wearing a beret") + .background(ImageEditParams.Background.TRANSPARENT) .mask("some content".byteInputStream()) .model(ImageModel.DALL_E_2) .n(1L) From a751d25d007ce1c962665f572e37c256d6921e60 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 19:13:48 +0000 Subject: [PATCH 4/4] release: 1.6.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4a2f7e60..7deae338 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.5.1" + ".": "1.6.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b32da4c..e7a831f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.6.0 (2025-05-02) + +Full Changelog: [v1.5.1...v1.6.0](https://github.com/openai/openai-java/compare/v1.5.1...v1.6.0) + +### Features + +* **api:** add image sizes, reasoning encryption ([a4a7d53](https://github.com/openai/openai-java/commit/a4a7d5390245ba11afe1cbd8665e0e39af3ca16f)) + + +### Chores + +* **internal:** update java toolchain ([eef415d](https://github.com/openai/openai-java/commit/eef415d067c9dd6ac6d4b8c825f4d068a6c4a63c)) + + +### Documentation + +* add `ImageGenerationExample` ([#457](https://github.com/openai/openai-java/issues/457)) ([286b7d5](https://github.com/openai/openai-java/commit/286b7d52d8cb917130579352a89afd5f8c839894)) + ## 1.5.1 (2025-04-27) Full Changelog: [v1.5.0...v1.5.1](https://github.com/openai/openai-java/compare/v1.5.0...v1.5.1) diff --git a/README.md b/README.md index cc0b5591..e6b82333 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/1.5.1) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/1.5.1/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/1.5.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/1.6.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/1.6.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/1.6.0) @@ -11,7 +11,7 @@ The OpenAI Java SDK provides convenient access to the [OpenAI REST API](https:// -The 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.5.1). +The 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.6.0). @@ -22,7 +22,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor ### Gradle ```kotlin -implementation("com.openai:openai-java:1.5.1") +implementation("com.openai:openai-java:1.6.0") ``` ### Maven @@ -31,7 +31,7 @@ implementation("com.openai:openai-java:1.5.1") com.openai openai-java - 1.5.1 + 1.6.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 0f55d5cb..c74dcef5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "1.5.1" // x-release-please-version + version = "1.6.0" // x-release-please-version } subprojects {