From 542fe24326bfe7c15d8889e8e249085c9a0bb4fa Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Thu, 29 Oct 2026 14:13:04 +0000 Subject: [PATCH] feat(api): add image edit markup overlays Castiron-Internal-PR: https://github.com/openai/openai-java-internal/pull/400 Castiron-Source-SHA: 7b4f0eb8e67ee74d6b8377d9d58aa32bcb7a705b Castiron-Public-Base-SHA: 1fc6935fc4a5258cb1910151a06a38ff223b4f33 --- .castiron.stats.yml | 10 +- api_reference/openapi.transformed.yml | 9 ++ .../openai/models/images/ImageEditParams.kt | 112 +++++++++++++++++- .../models/images/ImageEditParamsTest.kt | 3 + ...ServiceAsyncStreamingServerContractTest.kt | 1 + .../services/async/ImageServiceAsyncTest.kt | 1 + ...ImageServiceStreamingServerContractTest.kt | 1 + .../services/blocking/ImageServiceTest.kt | 1 + scripts/castiron/generated-files.json | 12 +- 9 files changed, 137 insertions(+), 13 deletions(-) diff --git a/.castiron.stats.yml b/.castiron.stats.yml index 5d5545dc9..854608b18 100644 --- a/.castiron.stats.yml +++ b/.castiron.stats.yml @@ -1,7 +1,7 @@ schema_version: 1 -generation_id: 05fecd67-8010-4180-820f-6c1c5ce9856f -openapi_spec_hash: d6b775fd4147ead7b31c9a5fd9ccb451 -openapi_transformed_spec_hash: 377739d6e90924beb54118b4f92c2d34 +generation_id: f129221a-b2b2-4a04-8fb5-bc9ae13a7385 +openapi_spec_hash: 172b67425fa2f92adad8d6ffac9968bd +openapi_transformed_spec_hash: 65ec527530a65399ed22f0a46cd03cd9 config_hash: 4e0613192a6ffb1791a25180a99a769c -codegen_sha: 14d7cb536cd77da5aaa8cf86665de29447aebfad -codegen_hash: 418de0017de1f7c46db51b70756fe173fa2b854f7dd4873624b0d46639b0a75b +codegen_sha: 924bd07d3d31b02eab58145f7aea65676e41a32e +codegen_hash: a82daaf0c7cbb8da5c442b1b5b85d86c5ebd1df1115786926274e8d148747111 diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml index 0d1f9c7a3..12bf6ad70 100644 --- a/api_reference/openapi.transformed.yml +++ b/api_reference/openapi.transformed.yml @@ -44425,6 +44425,8 @@ components: example: high description: | The quality of the image that will be generated for GPT image models. The GPT image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`. + markup: + $ref: '#/components/schemas/markup' required: - prompt - image @@ -46991,6 +46993,9 @@ components: description: Stream partial image results as events. partial_images: $ref: '#/components/schemas/PartialImages' + markup: + $ref: '#/components/schemas/ImageRefParam' + description: A transparent PNG overlaid on the first image, referenced by file ID, HTTPS URL, or base64 data URL. Must match that image's dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and 2.6. Cannot be combined with mask. required: - images - prompt @@ -111067,6 +111072,10 @@ components: - async additionalProperties: false description: The effective settings for compacting the agent's context. + markup: + type: string + format: binary + description: A transparent PNG overlaid on the first image to guide the edit. Must match that image's dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and 2.6. Cannot be combined with mask. SessionSpendControlResource: type: object properties: 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 fc1321c84..3a40fa094 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 @@ -91,6 +91,16 @@ private constructor( */ fun inputFidelity(): Optional = body.inputFidelity() + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that image's + * dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and 2.6. + * Cannot be combined with mask. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun markup(): Optional = body.markup() + /** * 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 @@ -241,6 +251,13 @@ private constructor( */ fun _inputFidelity(): MultipartField = body._inputFidelity() + /** + * Returns the raw multipart value of [markup]. + * + * Unlike [markup], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _markup(): MultipartField = body._markup() + /** * Returns the raw multipart value of [mask]. * @@ -371,7 +388,7 @@ private constructor( * - [prompt] * - [background] * - [inputFidelity] - * - [mask] + * - [markup] * - etc. */ fun body(body: Body) = apply { this.body = body.toBuilder() } @@ -491,6 +508,36 @@ private constructor( body.inputFidelity(inputFidelity) } + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that image's + * dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and + * 2.6. Cannot be combined with mask. + */ + fun markup(markup: InputStream) = apply { body.markup(markup) } + + /** + * Sets [Builder.markup] to an arbitrary multipart value. + * + * You should usually call [Builder.markup] with a well-typed [InputStream] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun markup(markup: MultipartField) = apply { body.markup(markup) } + + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that image's + * dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and + * 2.6. Cannot be combined with mask. + */ + fun markup(markup: ByteArray) = apply { body.markup(markup) } + + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that image's + * dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and + * 2.6. Cannot be combined with mask. + */ + fun markup(path: Path) = apply { body.markup(path) } + /** * 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 @@ -918,6 +965,7 @@ private constructor( "prompt" to _prompt(), "background" to _background(), "input_fidelity" to _inputFidelity(), + "markup" to _markup(), "mask" to _mask(), "model" to _model(), "n" to _n(), @@ -942,6 +990,7 @@ private constructor( private val prompt: MultipartField, private val background: MultipartField, private val inputFidelity: MultipartField, + private val markup: MultipartField, private val mask: MultipartField, private val model: MultipartField, private val n: MultipartField, @@ -1007,6 +1056,16 @@ private constructor( fun inputFidelity(): Optional = inputFidelity.value.getOptional("input_fidelity") + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that image's + * dimensions after orientation and contain transparent pixels. Supports GPT Image 2.5 and + * 2.6. Cannot be combined with mask. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun markup(): Optional = markup.value.getOptional("markup") + /** * 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 @@ -1168,6 +1227,13 @@ private constructor( @ExcludeMissing fun _inputFidelity(): MultipartField = inputFidelity + /** + * Returns the raw multipart value of [markup]. + * + * Unlike [markup], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("markup") @ExcludeMissing fun _markup(): MultipartField = markup + /** * Returns the raw multipart value of [mask]. * @@ -1298,6 +1364,7 @@ private constructor( private var prompt: MultipartField? = null private var background: MultipartField = MultipartField.of(null) private var inputFidelity: MultipartField = MultipartField.of(null) + private var markup: 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) @@ -1317,6 +1384,7 @@ private constructor( prompt = body.prompt background = body.background inputFidelity = body.inputFidelity + markup = body.markup mask = body.mask model = body.model n = body.n @@ -1462,6 +1530,42 @@ private constructor( this.inputFidelity = inputFidelity } + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that + * image's dimensions after orientation and contain transparent pixels. Supports GPT + * Image 2.5 and 2.6. Cannot be combined with mask. + */ + fun markup(markup: InputStream) = markup(MultipartField.of(markup)) + + /** + * Sets [Builder.markup] to an arbitrary multipart value. + * + * You should usually call [Builder.markup] with a well-typed [InputStream] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun markup(markup: MultipartField) = apply { this.markup = markup } + + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that + * image's dimensions after orientation and contain transparent pixels. Supports GPT + * Image 2.5 and 2.6. Cannot be combined with mask. + */ + fun markup(markup: ByteArray) = markup(markup.inputStream()) + + /** + * A transparent PNG overlaid on the first image to guide the edit. Must match that + * image's dimensions after orientation and contain transparent pixels. Supports GPT + * Image 2.5 and 2.6. Cannot be combined with mask. + */ + fun markup(path: Path) = + markup( + MultipartField.builder() + .value(path.inputStream()) + .filename(path.name) + .build() + ) + /** * 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 @@ -1807,6 +1911,7 @@ private constructor( checkRequired("prompt", prompt), background, inputFidelity, + markup, mask, model, n, @@ -1842,6 +1947,7 @@ private constructor( prompt() background().ifPresent { it.validate() } inputFidelity().ifPresent { it.validate() } + markup() mask() model().ifPresent { it.validate() } n() @@ -1874,6 +1980,7 @@ private constructor( prompt == other.prompt && background == other.background && inputFidelity == other.inputFidelity && + markup == other.markup && mask == other.mask && model == other.model && n == other.n && @@ -1894,6 +2001,7 @@ private constructor( prompt, background, inputFidelity, + markup, mask, model, n, @@ -1912,7 +2020,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Body{image=$image, prompt=$prompt, background=$background, inputFidelity=$inputFidelity, mask=$mask, model=$model, n=$n, outputCompression=$outputCompression, outputFormat=$outputFormat, partialImages=$partialImages, quality=$quality, responseFormat=$responseFormat, safetyIdentifier=$safetyIdentifier, size=$size, user=$user, additionalProperties=$additionalProperties}" + "Body{image=$image, prompt=$prompt, background=$background, inputFidelity=$inputFidelity, markup=$markup, mask=$mask, model=$model, n=$n, outputCompression=$outputCompression, outputFormat=$outputFormat, partialImages=$partialImages, quality=$quality, responseFormat=$responseFormat, safetyIdentifier=$safetyIdentifier, size=$size, user=$user, additionalProperties=$additionalProperties}" } /** 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 eb40d318d..8f2e1f379 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 @@ -16,6 +16,7 @@ internal class ImageEditParamsTest { .prompt("A cute baby sea otter wearing a beret") .background(ImageEditParams.Background.TRANSPARENT) .inputFidelity(ImageEditParams.InputFidelity.HIGH) + .markup("Example data".byteInputStream()) .mask("Example data".byteInputStream()) .model(ImageModel.GPT_IMAGE_1_5) .n(1L) @@ -38,6 +39,7 @@ internal class ImageEditParamsTest { .prompt("A cute baby sea otter wearing a beret") .background(ImageEditParams.Background.TRANSPARENT) .inputFidelity(ImageEditParams.InputFidelity.HIGH) + .markup("Example data".byteInputStream()) .mask("Example data".byteInputStream()) .model(ImageModel.GPT_IMAGE_1_5) .n(1L) @@ -75,6 +77,7 @@ internal class ImageEditParamsTest { "prompt" to MultipartField.of("A cute baby sea otter wearing a beret"), "background" to MultipartField.of(ImageEditParams.Background.TRANSPARENT), "input_fidelity" to MultipartField.of(ImageEditParams.InputFidelity.HIGH), + "markup" to MultipartField.of("Example data".byteInputStream()), "mask" to MultipartField.of("Example data".byteInputStream()), "model" to MultipartField.of(ImageModel.GPT_IMAGE_1_5), "n" to MultipartField.of(1L), diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncStreamingServerContractTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncStreamingServerContractTest.kt index a0cf0f9b0..e1e9c84ae 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncStreamingServerContractTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncStreamingServerContractTest.kt @@ -30,6 +30,7 @@ internal class ImageServiceAsyncStreamingServerContractTest { .prompt("A cute baby sea otter wearing a beret") .background(ImageEditParams.Background.TRANSPARENT) .inputFidelity(ImageEditParams.InputFidelity.HIGH) + .markup("Example data".byteInputStream()) .mask("Example data".byteInputStream()) .model(ImageModel.GPT_IMAGE_1_5) .n(1L) 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 b9de7585e..d56a84088 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 @@ -57,6 +57,7 @@ internal class ImageServiceAsyncTest { .prompt("A cute baby sea otter wearing a beret") .background(ImageEditParams.Background.TRANSPARENT) .inputFidelity(ImageEditParams.InputFidelity.HIGH) + .markup("Example data".byteInputStream()) .mask("Example data".byteInputStream()) .model(ImageModel.GPT_IMAGE_1_5) .n(1L) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceStreamingServerContractTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceStreamingServerContractTest.kt index 1cdabb594..01571497f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceStreamingServerContractTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceStreamingServerContractTest.kt @@ -30,6 +30,7 @@ internal class ImageServiceStreamingServerContractTest { .prompt("A cute baby sea otter wearing a beret") .background(ImageEditParams.Background.TRANSPARENT) .inputFidelity(ImageEditParams.InputFidelity.HIGH) + .markup("Example data".byteInputStream()) .mask("Example data".byteInputStream()) .model(ImageModel.GPT_IMAGE_1_5) .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 50ba26bd0..6af9bdf9e 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 @@ -56,6 +56,7 @@ internal class ImageServiceTest { .prompt("A cute baby sea otter wearing a beret") .background(ImageEditParams.Background.TRANSPARENT) .inputFidelity(ImageEditParams.InputFidelity.HIGH) + .markup("Example data".byteInputStream()) .mask("Example data".byteInputStream()) .model(ImageModel.GPT_IMAGE_1_5) .n(1L) diff --git a/scripts/castiron/generated-files.json b/scripts/castiron/generated-files.json index 2758c08b3..4838681e7 100644 --- a/scripts/castiron/generated-files.json +++ b/scripts/castiron/generated-files.json @@ -4487,7 +4487,7 @@ }, "openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt": { "mode": "100644", - "sha256": "a9e4b29fe53c8df58ef286d4b395973a7e20747a0becf6a97a704f2ae45f3f20" + "sha256": "caa2bb980b02706a8963048d0a3a7100da5950cd6d8cff1ad3bdf8c3705f7b06" }, "openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditPartialImageEvent.kt": { "mode": "100644", @@ -12527,7 +12527,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/images/ImageEditParamsTest.kt": { "mode": "100644", - "sha256": "52fc9f3e797210d398cc76c1cbe1e4caa59e6c7f5f9e7b39b0a7ce3a19107317" + "sha256": "96f5163da9f66557a030e1cc20ac8fab45ad58687a09a55a7a2a1e72f5b8f2c3" }, "openai-java-core/src/test/kotlin/com/openai/models/images/ImageEditPartialImageEventTest.kt": { "mode": "100644", @@ -14631,11 +14631,11 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncStreamingServerContractTest.kt": { "mode": "100644", - "sha256": "d5f4de00265815a7162d68566a06903d8d064a36f37166e427eab1abbac61d4b" + "sha256": "238df5c19d81796e3ee1aefefeccea642f1663ef340e573bbe612e4675410b8f" }, "openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt": { "mode": "100644", - "sha256": "b1234e1d94b6565d1626306e7438e0fe846dbe1113e166e2706c2eaa3c742824" + "sha256": "4edb54b523e952d9c3936839472cdf396b54a7c7fd99bb3beac35bcd713ff8c3" }, "openai-java-core/src/test/kotlin/com/openai/services/async/LiveServiceAsyncTest.kt": { "mode": "100644", @@ -15371,11 +15371,11 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceStreamingServerContractTest.kt": { "mode": "100644", - "sha256": "f9d398a0aa48d59387de7d75b5b339246103bfe17814187eb4aed17cdafb0ece" + "sha256": "2e36fb4aae28b7b5c5764fc720cbe1808eedf51265d275f83f3ef090b93a7228" }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt": { "mode": "100644", - "sha256": "397da1a8b02cb8d4a79f890e58873f0b2fc7da1442b81aa97a56d574fa9749e4" + "sha256": "72ef6396a7894a93deb166350839db0d6b66047f61cd698966dc33409f8f0c2b" }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/LiveServiceTest.kt": { "mode": "100644",