From e89596d624fd77fdef750fb65a9ca5c6497e76cb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 21:10:50 +0000 Subject: [PATCH 1/4] chore(internal): refactor delegating from client to options --- .../client/okhttp/OpenAIOkHttpClient.kt | 104 +++++++++--------- .../client/okhttp/OpenAIOkHttpClientAsync.kt | 104 +++++++++--------- .../kotlin/com/openai/core/ClientOptions.kt | 26 +++++ 3 files changed, 132 insertions(+), 102 deletions(-) diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt index 6d34a73c..15203673 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt @@ -10,6 +10,7 @@ import com.openai.core.ClientOptions import com.openai.core.Timeout import com.openai.core.http.Headers import com.openai.core.http.QueryParams +import com.openai.core.jsonMapper import com.openai.credential.Credential import java.net.Proxy import java.time.Clock @@ -32,10 +33,9 @@ class OpenAIOkHttpClient private constructor() { class Builder internal constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() - private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null - fun baseUrl(baseUrl: String) = apply { clientOptions.baseUrl(baseUrl) } + fun proxy(proxy: Proxy) = apply { this.proxy = proxy } /** * Whether to throw an exception if any of the Jackson versions detected at runtime are @@ -56,6 +56,54 @@ class OpenAIOkHttpClient private constructor() { fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) } + + /** Alias for calling [Builder.baseUrl] with `baseUrl.orElse(null)`. */ + fun baseUrl(baseUrl: Optional) = baseUrl(baseUrl.getOrNull()) + + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) } + + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } + + fun credential(credential: Credential) = apply { clientOptions.credential(credential) } + + fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply { + clientOptions.azureServiceVersion(azureServiceVersion) + } + + fun organization(organization: String?) = apply { clientOptions.organization(organization) } + + /** Alias for calling [Builder.organization] with `organization.orElse(null)`. */ + fun organization(organization: Optional) = organization(organization.getOrNull()) + + fun project(project: String?) = apply { clientOptions.project(project) } + + /** Alias for calling [Builder.project] with `project.orElse(null)`. */ + fun project(project: Optional) = project(project.getOrNull()) + + fun webhookSecret(webhookSecret: String?) = apply { + clientOptions.webhookSecret(webhookSecret) + } + + /** Alias for calling [Builder.webhookSecret] with `webhookSecret.orElse(null)`. */ + fun webhookSecret(webhookSecret: Optional) = + webhookSecret(webhookSecret.getOrNull()) + fun headers(headers: Headers) = apply { clientOptions.headers(headers) } fun headers(headers: Map>) = apply { @@ -136,54 +184,6 @@ class OpenAIOkHttpClient private constructor() { clientOptions.removeAllQueryParams(keys) } - fun timeout(timeout: Timeout) = apply { - clientOptions.timeout(timeout) - this.timeout = timeout - } - - /** - * Sets the maximum time allowed for a complete HTTP call, not including retries. - * - * See [Timeout.request] for more details. - * - * For fine-grained control, pass a [Timeout] object. - */ - fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) - - fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } - - fun proxy(proxy: Proxy) = apply { this.proxy = proxy } - - fun responseValidation(responseValidation: Boolean) = apply { - clientOptions.responseValidation(responseValidation) - } - - fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } - - fun credential(credential: Credential) = apply { clientOptions.credential(credential) } - - fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply { - clientOptions.azureServiceVersion(azureServiceVersion) - } - - fun organization(organization: String?) = apply { clientOptions.organization(organization) } - - /** Alias for calling [Builder.organization] with `organization.orElse(null)`. */ - fun organization(organization: Optional) = organization(organization.getOrNull()) - - fun project(project: String?) = apply { clientOptions.project(project) } - - /** Alias for calling [Builder.project] with `project.orElse(null)`. */ - fun project(project: Optional) = project(project.getOrNull()) - - fun webhookSecret(webhookSecret: String?) = apply { - clientOptions.webhookSecret(webhookSecret) - } - - /** Alias for calling [Builder.webhookSecret] with `webhookSecret.orElse(null)`. */ - fun webhookSecret(webhookSecret: Optional) = - webhookSecret(webhookSecret.getOrNull()) - fun fromEnv() = apply { clientOptions.fromEnv() } /** @@ -194,7 +194,9 @@ class OpenAIOkHttpClient private constructor() { fun build(): OpenAIClient = OpenAIClientImpl( clientOptions - .httpClient(OkHttpClient.builder().timeout(timeout).proxy(proxy).build()) + .httpClient( + OkHttpClient.builder().timeout(clientOptions.timeout()).proxy(proxy).build() + ) .build() ) } diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt index ad00f0d9..2bcbd03e 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ClientOptions import com.openai.core.Timeout import com.openai.core.http.Headers import com.openai.core.http.QueryParams +import com.openai.core.jsonMapper import com.openai.credential.Credential import java.net.Proxy import java.time.Clock @@ -32,10 +33,9 @@ class OpenAIOkHttpClientAsync private constructor() { class Builder internal constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() - private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null - fun baseUrl(baseUrl: String) = apply { clientOptions.baseUrl(baseUrl) } + fun proxy(proxy: Proxy) = apply { this.proxy = proxy } /** * Whether to throw an exception if any of the Jackson versions detected at runtime are @@ -56,6 +56,54 @@ class OpenAIOkHttpClientAsync private constructor() { fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) } + + /** Alias for calling [Builder.baseUrl] with `baseUrl.orElse(null)`. */ + fun baseUrl(baseUrl: Optional) = baseUrl(baseUrl.getOrNull()) + + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) } + + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } + + fun credential(credential: Credential) = apply { clientOptions.credential(credential) } + + fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply { + clientOptions.azureServiceVersion(azureServiceVersion) + } + + fun organization(organization: String?) = apply { clientOptions.organization(organization) } + + /** Alias for calling [Builder.organization] with `organization.orElse(null)`. */ + fun organization(organization: Optional) = organization(organization.getOrNull()) + + fun project(project: String?) = apply { clientOptions.project(project) } + + /** Alias for calling [Builder.project] with `project.orElse(null)`. */ + fun project(project: Optional) = project(project.getOrNull()) + + fun webhookSecret(webhookSecret: String?) = apply { + clientOptions.webhookSecret(webhookSecret) + } + + /** Alias for calling [Builder.webhookSecret] with `webhookSecret.orElse(null)`. */ + fun webhookSecret(webhookSecret: Optional) = + webhookSecret(webhookSecret.getOrNull()) + fun headers(headers: Headers) = apply { clientOptions.headers(headers) } fun headers(headers: Map>) = apply { @@ -136,54 +184,6 @@ class OpenAIOkHttpClientAsync private constructor() { clientOptions.removeAllQueryParams(keys) } - fun timeout(timeout: Timeout) = apply { - clientOptions.timeout(timeout) - this.timeout = timeout - } - - /** - * Sets the maximum time allowed for a complete HTTP call, not including retries. - * - * See [Timeout.request] for more details. - * - * For fine-grained control, pass a [Timeout] object. - */ - fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) - - fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } - - fun proxy(proxy: Proxy) = apply { this.proxy = proxy } - - fun responseValidation(responseValidation: Boolean) = apply { - clientOptions.responseValidation(responseValidation) - } - - fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } - - fun credential(credential: Credential) = apply { clientOptions.credential(credential) } - - fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply { - clientOptions.azureServiceVersion(azureServiceVersion) - } - - fun organization(organization: String?) = apply { clientOptions.organization(organization) } - - /** Alias for calling [Builder.organization] with `organization.orElse(null)`. */ - fun organization(organization: Optional) = organization(organization.getOrNull()) - - fun project(project: String?) = apply { clientOptions.project(project) } - - /** Alias for calling [Builder.project] with `project.orElse(null)`. */ - fun project(project: Optional) = project(project.getOrNull()) - - fun webhookSecret(webhookSecret: String?) = apply { - clientOptions.webhookSecret(webhookSecret) - } - - /** Alias for calling [Builder.webhookSecret] with `webhookSecret.orElse(null)`. */ - fun webhookSecret(webhookSecret: Optional) = - webhookSecret(webhookSecret.getOrNull()) - fun fromEnv() = apply { clientOptions.fromEnv() } /** @@ -194,7 +194,9 @@ class OpenAIOkHttpClientAsync private constructor() { fun build(): OpenAIClientAsync = OpenAIClientAsyncImpl( clientOptions - .httpClient(OkHttpClient.builder().timeout(timeout).proxy(proxy).build()) + .httpClient( + OkHttpClient.builder().timeout(clientOptions.timeout()).proxy(proxy).build() + ) .build() ) } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt b/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt index 9e125507..cf847f31 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt @@ -13,6 +13,7 @@ import com.openai.core.http.RetryingHttpClient import com.openai.credential.BearerTokenCredential import com.openai.credential.Credential import java.time.Clock +import java.time.Duration import java.util.Optional import java.util.concurrent.Executor import java.util.concurrent.Executors @@ -24,6 +25,13 @@ class ClientOptions private constructor( private val originalHttpClient: HttpClient, @get:JvmName("httpClient") val httpClient: HttpClient, + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee that + * the SDK will work correctly when using an incompatible Jackson version. + */ @get:JvmName("checkJacksonVersionCompatibility") val checkJacksonVersionCompatibility: Boolean, @get:JvmName("jsonMapper") val jsonMapper: JsonMapper, @get:JvmName("streamHandlerExecutor") val streamHandlerExecutor: Executor, @@ -119,6 +127,13 @@ private constructor( this.httpClient = PhantomReachableClosingHttpClient(httpClient) } + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { this.checkJacksonVersionCompatibility = checkJacksonVersionCompatibility } @@ -142,6 +157,15 @@ private constructor( fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } fun apiKey(apiKey: String) = apply { @@ -250,6 +274,8 @@ private constructor( fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) } + fun timeout(): Timeout = timeout + fun fromEnv() = apply { System.getenv("OPENAI_BASE_URL")?.let { baseUrl(it) } From 220503efc745bd1c74c02f1d4d6d3305198bf942 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 22:37:52 +0000 Subject: [PATCH 2/4] feat(client): allow setting additional multipart body props --- .../TranscriptionCreateParams.kt | 64 +++++++++++++-- .../translations/TranslationCreateParams.kt | 68 ++++++++++++++-- .../containers/files/FileCreateParams.kt | 70 ++++++++++++++-- .../openai/models/files/FileCreateParams.kt | 75 +++++++++++++++-- .../images/ImageCreateVariationParams.kt | 77 ++++++++++++++++-- .../openai/models/images/ImageEditParams.kt | 67 ++++++++++++++-- .../models/uploads/parts/PartCreateParams.kt | 80 +++++++++++++++++-- 7 files changed, 463 insertions(+), 38 deletions(-) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt index 226cf34a..29712039 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt @@ -209,6 +209,8 @@ private constructor( fun _timestampGranularities(): MultipartField> = body._timestampGranularities() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -466,6 +468,25 @@ private constructor( body.addTimestampGranularity(timestampGranularity) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -586,7 +607,7 @@ private constructor( } fun _body(): Map> = - mapOf( + (mapOf( "file" to _file(), "model" to _model(), "chunking_strategy" to _chunkingStrategy(), @@ -596,7 +617,7 @@ private constructor( "response_format" to _responseFormat(), "temperature" to _temperature(), "timestamp_granularities" to _timestampGranularities(), - ) + ) + _additionalBodyProperties().mapValues { MultipartField.of(it) }) .toImmutable() override fun _headers(): Headers = additionalHeaders @@ -614,6 +635,7 @@ private constructor( private val responseFormat: MultipartField, private val temperature: MultipartField, private val timestampGranularities: MultipartField>, + private val additionalProperties: MutableMap, ) { /** @@ -792,6 +814,16 @@ private constructor( fun _timestampGranularities(): MultipartField> = timestampGranularities + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -822,6 +854,7 @@ private constructor( private var temperature: MultipartField = MultipartField.of(null) private var timestampGranularities: MultipartField>? = null + private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { @@ -834,6 +867,7 @@ private constructor( responseFormat = body.responseFormat temperature = body.temperature timestampGranularities = body.timestampGranularities.map { it.toMutableList() } + additionalProperties = body.additionalProperties.toMutableMap() } /** @@ -1065,6 +1099,25 @@ private constructor( } } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * @@ -1089,6 +1142,7 @@ private constructor( responseFormat, temperature, (timestampGranularities ?: MultipartField.of(null)).map { it.toImmutable() }, + additionalProperties.toMutableMap(), ) } @@ -1124,17 +1178,17 @@ private constructor( return true } - return /* spotless:off */ other is Body && file == other.file && model == other.model && chunkingStrategy == other.chunkingStrategy && include == other.include && language == other.language && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature && timestampGranularities == other.timestampGranularities /* spotless:on */ + return /* spotless:off */ other is Body && file == other.file && model == other.model && chunkingStrategy == other.chunkingStrategy && include == other.include && language == other.language && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature && timestampGranularities == other.timestampGranularities && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(file, model, chunkingStrategy, include, language, prompt, responseFormat, temperature, timestampGranularities) } + private val hashCode: Int by lazy { Objects.hash(file, model, chunkingStrategy, include, language, prompt, responseFormat, temperature, timestampGranularities, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Body{file=$file, model=$model, chunkingStrategy=$chunkingStrategy, include=$include, language=$language, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature, timestampGranularities=$timestampGranularities}" + "Body{file=$file, model=$model, chunkingStrategy=$chunkingStrategy, include=$include, language=$language, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature, timestampGranularities=$timestampGranularities, additionalProperties=$additionalProperties}" } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt index 7ce0911d..2ee01950 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt @@ -2,11 +2,14 @@ package com.openai.models.audio.translations +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import com.openai.core.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField +import com.openai.core.JsonValue import com.openai.core.MultipartField import com.openai.core.Params import com.openai.core.checkRequired @@ -17,6 +20,7 @@ import com.openai.errors.OpenAIInvalidDataException import com.openai.models.audio.AudioModel import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.io.path.inputStream @@ -115,6 +119,8 @@ private constructor( */ fun _temperature(): MultipartField = body._temperature() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -267,6 +273,25 @@ private constructor( body.temperature(temperature) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -387,13 +412,13 @@ private constructor( } fun _body(): Map> = - mapOf( + (mapOf( "file" to _file(), "model" to _model(), "prompt" to _prompt(), "response_format" to _responseFormat(), "temperature" to _temperature(), - ) + ) + _additionalBodyProperties().mapValues { MultipartField.of(it) }) .toImmutable() override fun _headers(): Headers = additionalHeaders @@ -407,6 +432,7 @@ private constructor( private val prompt: MultipartField, private val responseFormat: MultipartField, private val temperature: MultipartField, + private val additionalProperties: MutableMap, ) { /** @@ -500,6 +526,16 @@ private constructor( @ExcludeMissing fun _temperature(): MultipartField = temperature + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -524,6 +560,7 @@ private constructor( private var prompt: MultipartField = MultipartField.of(null) private var responseFormat: MultipartField = MultipartField.of(null) private var temperature: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { @@ -532,6 +569,7 @@ private constructor( prompt = body.prompt responseFormat = body.responseFormat temperature = body.temperature + additionalProperties = body.additionalProperties.toMutableMap() } /** @@ -645,6 +683,25 @@ private constructor( this.temperature = temperature } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * @@ -665,6 +722,7 @@ private constructor( prompt, responseFormat, temperature, + additionalProperties.toMutableMap(), ) } @@ -696,17 +754,17 @@ private constructor( return true } - return /* spotless:off */ other is Body && file == other.file && model == other.model && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature /* spotless:on */ + return /* spotless:off */ other is Body && file == other.file && model == other.model && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(file, model, prompt, responseFormat, temperature) } + private val hashCode: Int by lazy { Objects.hash(file, model, prompt, responseFormat, temperature, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Body{file=$file, model=$model, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature}" + "Body{file=$file, model=$model, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature, additionalProperties=$additionalProperties}" } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/containers/files/FileCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/containers/files/FileCreateParams.kt index 4d0db685..d41db32e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/containers/files/FileCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/containers/files/FileCreateParams.kt @@ -2,8 +2,11 @@ package com.openai.models.containers.files +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonProperty import com.openai.core.ExcludeMissing +import com.openai.core.JsonValue import com.openai.core.MultipartField import com.openai.core.Params import com.openai.core.http.Headers @@ -12,6 +15,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.io.path.inputStream @@ -64,6 +68,8 @@ private constructor( */ fun _fileId(): MultipartField = body._fileId() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -138,6 +144,25 @@ private constructor( */ fun fileId(fileId: MultipartField) = apply { body.fileId(fileId) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -251,7 +276,9 @@ private constructor( } fun _body(): Map> = - mapOf("file" to _file(), "file_id" to _fileId()).toImmutable() + (mapOf("file" to _file(), "file_id" to _fileId()) + + _additionalBodyProperties().mapValues { MultipartField.of(it) }) + .toImmutable() fun _pathParam(index: Int): String = when (index) { @@ -267,6 +294,7 @@ private constructor( private constructor( private val file: MultipartField, private val fileId: MultipartField, + private val additionalProperties: MutableMap, ) { /** @@ -299,6 +327,16 @@ private constructor( */ @JsonProperty("file_id") @ExcludeMissing fun _fileId(): MultipartField = fileId + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -312,11 +350,13 @@ private constructor( private var file: MultipartField = MultipartField.of(null) private var fileId: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { file = body.file fileId = body.fileId + additionalProperties = body.additionalProperties.toMutableMap() } /** The File object (not file name) to be uploaded. */ @@ -355,12 +395,31 @@ private constructor( */ fun fileId(fileId: MultipartField) = apply { this.fileId = fileId } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Body = Body(file, fileId) + fun build(): Body = Body(file, fileId, additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -388,16 +447,17 @@ private constructor( return true } - return /* spotless:off */ other is Body && file == other.file && fileId == other.fileId /* spotless:on */ + return /* spotless:off */ other is Body && file == other.file && fileId == other.fileId && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(file, fileId) } + private val hashCode: Int by lazy { Objects.hash(file, fileId, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode - override fun toString() = "Body{file=$file, fileId=$fileId}" + override fun toString() = + "Body{file=$file, fileId=$fileId, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt index b8798287..d91219f9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt @@ -2,8 +2,11 @@ package com.openai.models.files +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonProperty import com.openai.core.ExcludeMissing +import com.openai.core.JsonValue import com.openai.core.MultipartField import com.openai.core.Params import com.openai.core.checkRequired @@ -13,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import kotlin.io.path.inputStream import kotlin.io.path.name @@ -74,6 +78,8 @@ private constructor( */ fun _purpose(): MultipartField = body._purpose() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -153,6 +159,25 @@ private constructor( */ fun purpose(purpose: MultipartField) = apply { body.purpose(purpose) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -269,7 +294,9 @@ private constructor( } fun _body(): Map> = - mapOf("file" to _file(), "purpose" to _purpose()).toImmutable() + (mapOf("file" to _file(), "purpose" to _purpose()) + + _additionalBodyProperties().mapValues { MultipartField.of(it) }) + .toImmutable() override fun _headers(): Headers = additionalHeaders @@ -279,6 +306,7 @@ private constructor( private constructor( private val file: MultipartField, private val purpose: MultipartField, + private val additionalProperties: MutableMap, ) { /** @@ -317,6 +345,16 @@ private constructor( @ExcludeMissing fun _purpose(): MultipartField = purpose + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -338,11 +376,13 @@ private constructor( private var file: MultipartField? = null private var purpose: MultipartField? = null + private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { file = body.file purpose = body.purpose + additionalProperties = body.additionalProperties.toMutableMap() } /** The File object (not file name) to be uploaded. */ @@ -386,6 +426,25 @@ private constructor( */ fun purpose(purpose: MultipartField) = apply { this.purpose = purpose } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * @@ -399,7 +458,12 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): Body = Body(checkRequired("file", file), checkRequired("purpose", purpose)) + fun build(): Body = + Body( + checkRequired("file", file), + checkRequired("purpose", purpose), + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -427,16 +491,17 @@ private constructor( return true } - return /* spotless:off */ other is Body && file == other.file && purpose == other.purpose /* spotless:on */ + return /* spotless:off */ other is Body && file == other.file && purpose == other.purpose && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(file, purpose) } + private val hashCode: Int by lazy { Objects.hash(file, purpose, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode - override fun toString() = "Body{file=$file, purpose=$purpose}" + override fun toString() = + "Body{file=$file, purpose=$purpose, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt index 66d53a78..4091857d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt @@ -2,11 +2,14 @@ package com.openai.models.images +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import com.openai.core.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField +import com.openai.core.JsonValue import com.openai.core.MultipartField import com.openai.core.Params import com.openai.core.checkRequired @@ -16,6 +19,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.io.path.inputStream @@ -125,6 +129,8 @@ private constructor( */ fun _user(): MultipartField = body._user() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -297,6 +303,25 @@ private constructor( */ fun user(user: MultipartField) = apply { body.user(user) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -416,14 +441,14 @@ private constructor( } fun _body(): Map> = - mapOf( + (mapOf( "image" to _image(), "model" to _model(), "n" to _n(), "response_format" to _responseFormat(), "size" to _size(), "user" to _user(), - ) + ) + _additionalBodyProperties().mapValues { MultipartField.of(it) }) .toImmutable() override fun _headers(): Headers = additionalHeaders @@ -438,6 +463,7 @@ private constructor( private val responseFormat: MultipartField, private val size: MultipartField, private val user: MultipartField, + private val additionalProperties: MutableMap, ) { /** @@ -538,6 +564,16 @@ private constructor( */ @JsonProperty("user") @ExcludeMissing fun _user(): MultipartField = user + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -562,6 +598,7 @@ private constructor( private var responseFormat: MultipartField = MultipartField.of(null) private var size: MultipartField = MultipartField.of(null) private var user: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { @@ -571,6 +608,7 @@ private constructor( responseFormat = body.responseFormat size = body.size user = body.user + additionalProperties = body.additionalProperties.toMutableMap() } /** @@ -708,6 +746,25 @@ private constructor( */ fun user(user: MultipartField) = apply { this.user = user } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * @@ -721,7 +778,15 @@ private constructor( * @throws IllegalStateException if any required field is unset. */ fun build(): Body = - Body(checkRequired("image", image), model, n, responseFormat, size, user) + Body( + checkRequired("image", image), + model, + n, + responseFormat, + size, + user, + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -753,17 +818,17 @@ private constructor( return true } - return /* spotless:off */ other is Body && image == other.image && model == other.model && n == other.n && responseFormat == other.responseFormat && size == other.size && user == other.user /* spotless:on */ + return /* spotless:off */ other is Body && image == other.image && model == other.model && n == other.n && responseFormat == other.responseFormat && size == other.size && user == other.user && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(image, model, n, responseFormat, size, user) } + private val hashCode: Int by lazy { Objects.hash(image, model, n, responseFormat, size, user, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Body{image=$image, model=$model, n=$n, responseFormat=$responseFormat, size=$size, user=$user}" + "Body{image=$image, model=$model, n=$n, responseFormat=$responseFormat, size=$size, user=$user, additionalProperties=$additionalProperties}" } /** 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 6872d5ee..18fe5c9d 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 @@ -2,6 +2,8 @@ package com.openai.models.images +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.core.JsonGenerator @@ -28,6 +30,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.io.path.inputStream @@ -290,6 +293,8 @@ private constructor( */ fun _user(): MultipartField = body._user() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -686,6 +691,25 @@ private constructor( */ fun user(user: MultipartField) = apply { body.user(user) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -802,7 +826,7 @@ private constructor( } fun _body(): Map> = - mapOf( + (mapOf( "image" to _image(), "prompt" to _prompt(), "background" to _background(), @@ -817,7 +841,7 @@ private constructor( "response_format" to _responseFormat(), "size" to _size(), "user" to _user(), - ) + ) + _additionalBodyProperties().mapValues { MultipartField.of(it) }) .toImmutable() override fun _headers(): Headers = additionalHeaders @@ -840,6 +864,7 @@ private constructor( private val responseFormat: MultipartField, private val size: MultipartField, private val user: MultipartField, + private val additionalProperties: MutableMap, ) { /** @@ -1106,6 +1131,16 @@ private constructor( */ @JsonProperty("user") @ExcludeMissing fun _user(): MultipartField = user + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -1139,6 +1174,7 @@ private constructor( private var responseFormat: MultipartField = MultipartField.of(null) private var size: MultipartField = MultipartField.of(null) private var user: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { @@ -1156,6 +1192,7 @@ private constructor( responseFormat = body.responseFormat size = body.size user = body.user + additionalProperties = body.additionalProperties.toMutableMap() } /** @@ -1529,6 +1566,25 @@ private constructor( */ fun user(user: MultipartField) = apply { this.user = user } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * @@ -1558,6 +1614,7 @@ private constructor( responseFormat, size, user, + additionalProperties.toMutableMap(), ) } @@ -1598,17 +1655,17 @@ private constructor( return true } - return /* spotless:off */ other is Body && image == other.image && prompt == other.prompt && background == other.background && inputFidelity == other.inputFidelity && mask == other.mask && model == other.model && n == other.n && outputCompression == other.outputCompression && outputFormat == other.outputFormat && partialImages == other.partialImages && 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 && inputFidelity == other.inputFidelity && mask == other.mask && model == other.model && n == other.n && outputCompression == other.outputCompression && outputFormat == other.outputFormat && partialImages == other.partialImages && quality == other.quality && responseFormat == other.responseFormat && size == other.size && user == other.user && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(image, prompt, background, inputFidelity, mask, model, n, outputCompression, outputFormat, partialImages, quality, responseFormat, size, user) } + private val hashCode: Int by lazy { Objects.hash(image, prompt, background, inputFidelity, mask, model, n, outputCompression, outputFormat, partialImages, quality, responseFormat, size, user, additionalProperties) } /* spotless:on */ 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, size=$size, user=$user}" + "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, size=$size, user=$user, additionalProperties=$additionalProperties}" } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt index 791713e4..b7cfa476 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt @@ -2,8 +2,11 @@ package com.openai.models.uploads.parts +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonProperty import com.openai.core.ExcludeMissing +import com.openai.core.JsonValue import com.openai.core.MultipartField import com.openai.core.Params import com.openai.core.checkRequired @@ -13,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.io.path.inputStream @@ -54,6 +58,8 @@ private constructor( */ fun _data(): MultipartField = body._data() + fun _additionalBodyProperties(): Map = body._additionalProperties() + fun _additionalHeaders(): Headers = additionalHeaders fun _additionalQueryParams(): QueryParams = additionalQueryParams @@ -121,6 +127,25 @@ private constructor( /** The chunk of bytes for this Part. */ fun data(data: Path) = apply { body.data(data) } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -240,7 +265,9 @@ private constructor( ) } - fun _body(): Map> = mapOf("data" to _data()).toImmutable() + fun _body(): Map> = + (mapOf("data" to _data()) + _additionalBodyProperties().mapValues { MultipartField.of(it) }) + .toImmutable() fun _pathParam(index: Int): String = when (index) { @@ -252,7 +279,11 @@ private constructor( override fun _queryParams(): QueryParams = additionalQueryParams - class Body private constructor(private val data: MultipartField) { + class Body + private constructor( + private val data: MultipartField, + private val additionalProperties: MutableMap, + ) { /** * The chunk of bytes for this Part. @@ -269,6 +300,16 @@ private constructor( */ @JsonProperty("data") @ExcludeMissing fun _data(): MultipartField = data + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + fun toBuilder() = Builder().from(this) companion object { @@ -288,8 +329,13 @@ private constructor( class Builder internal constructor() { private var data: MultipartField? = null + private var additionalProperties: MutableMap = mutableMapOf() - @JvmSynthetic internal fun from(body: Body) = apply { data = body.data } + @JvmSynthetic + internal fun from(body: Body) = apply { + data = body.data + additionalProperties = body.additionalProperties.toMutableMap() + } /** The chunk of bytes for this Part. */ fun data(data: InputStream) = data(MultipartField.of(data)) @@ -315,6 +361,25 @@ private constructor( .build() ) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + /** * Returns an immutable instance of [Body]. * @@ -327,7 +392,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): Body = Body(checkRequired("data", data)) + fun build(): Body = + Body(checkRequired("data", data), additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -354,16 +420,16 @@ private constructor( return true } - return /* spotless:off */ other is Body && data == other.data /* spotless:on */ + return /* spotless:off */ other is Body && data == other.data && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(data) } + private val hashCode: Int by lazy { Objects.hash(data, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode - override fun toString() = "Body{data=$data}" + override fun toString() = "Body{data=$data, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { From 3f745195fa5d72ee72653adbd8087915c0db2fa5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 22:46:20 +0000 Subject: [PATCH 3/4] feat(client): add https config options --- README.md | 21 ++++++ .../com/openai/client/okhttp/OkHttpClient.kt | 31 +++++++++ .../client/okhttp/OpenAIOkHttpClient.kt | 67 ++++++++++++++++++- .../client/okhttp/OpenAIOkHttpClientAsync.kt | 67 ++++++++++++++++++- 4 files changed, 182 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4cfacb47..82b88c4e 100644 --- a/README.md +++ b/README.md @@ -1398,6 +1398,27 @@ OpenAIClient client = OpenAIOkHttpClient.builder() .build(); ``` +### HTTPS + +> [!NOTE] +> Most applications should not call these methods, and instead use the system defaults. The defaults include +> special optimizations that can be lost if the implementations are modified. + +To configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods: + +```java +import com.openai.client.OpenAIClient; +import com.openai.client.okhttp.OpenAIOkHttpClient; + +OpenAIClient client = OpenAIOkHttpClient.builder() + .fromEnv() + // If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa. + .sslSocketFactory(yourSSLSocketFactory) + .trustManager(yourTrustManager) + .hostnameVerifier(yourHostnameVerifier) + .build(); +``` + ### Custom HTTP client The SDK consists of three artifacts: diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt index 5bc95418..e4f63c28 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt @@ -14,6 +14,9 @@ import java.io.InputStream import java.net.Proxy import java.time.Duration import java.util.concurrent.CompletableFuture +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager import okhttp3.Call import okhttp3.Callback import okhttp3.HttpUrl.Companion.toHttpUrl @@ -189,6 +192,9 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null fun timeout(timeout: Timeout) = apply { this.timeout = timeout } @@ -196,6 +202,18 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + fun build(): OkHttpClient = OkHttpClient( okhttp3.OkHttpClient.Builder() @@ -204,6 +222,19 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC .writeTimeout(timeout.write()) .callTimeout(timeout.request()) .proxy(proxy) + .apply { + val sslSocketFactory = sslSocketFactory + val trustManager = trustManager + if (sslSocketFactory != null && trustManager != null) { + sslSocketFactory(sslSocketFactory, trustManager) + } else { + check((sslSocketFactory != null) == (trustManager != null)) { + "Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set" + } + } + + hostnameVerifier?.let(::hostnameVerifier) + } .build() .apply { // We usually make all our requests to the same host so it makes sense to diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt index 15203673..95041bee 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt @@ -17,6 +17,9 @@ import java.time.Clock import java.time.Duration import java.util.Optional import java.util.concurrent.Executor +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager import kotlin.jvm.optionals.getOrNull class OpenAIOkHttpClient private constructor() { @@ -34,8 +37,62 @@ class OpenAIOkHttpClient private constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() private var proxy: Proxy? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null - fun proxy(proxy: Proxy) = apply { this.proxy = proxy } + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + + /** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */ + fun proxy(proxy: Optional) = proxy(proxy.getOrNull()) + + /** + * The socket factory used to secure HTTPS connections. + * + * If this is set, then [trustManager] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + /** Alias for calling [Builder.sslSocketFactory] with `sslSocketFactory.orElse(null)`. */ + fun sslSocketFactory(sslSocketFactory: Optional) = + sslSocketFactory(sslSocketFactory.getOrNull()) + + /** + * The trust manager used to secure HTTPS connections. + * + * If this is set, then [sslSocketFactory] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + /** Alias for calling [Builder.trustManager] with `trustManager.orElse(null)`. */ + fun trustManager(trustManager: Optional) = + trustManager(trustManager.getOrNull()) + + /** + * The verifier used to confirm that response certificates apply to requested hostnames for + * HTTPS connections. + * + * If unset, then a default hostname verifier is used. + */ + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + /** Alias for calling [Builder.hostnameVerifier] with `hostnameVerifier.orElse(null)`. */ + fun hostnameVerifier(hostnameVerifier: Optional) = + hostnameVerifier(hostnameVerifier.getOrNull()) /** * Whether to throw an exception if any of the Jackson versions detected at runtime are @@ -195,7 +252,13 @@ class OpenAIOkHttpClient private constructor() { OpenAIClientImpl( clientOptions .httpClient( - OkHttpClient.builder().timeout(clientOptions.timeout()).proxy(proxy).build() + OkHttpClient.builder() + .timeout(clientOptions.timeout()) + .proxy(proxy) + .sslSocketFactory(sslSocketFactory) + .trustManager(trustManager) + .hostnameVerifier(hostnameVerifier) + .build() ) .build() ) diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt index 2bcbd03e..06f5af79 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt @@ -17,6 +17,9 @@ import java.time.Clock import java.time.Duration import java.util.Optional import java.util.concurrent.Executor +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager import kotlin.jvm.optionals.getOrNull class OpenAIOkHttpClientAsync private constructor() { @@ -34,8 +37,62 @@ class OpenAIOkHttpClientAsync private constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() private var proxy: Proxy? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null - fun proxy(proxy: Proxy) = apply { this.proxy = proxy } + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + + /** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */ + fun proxy(proxy: Optional) = proxy(proxy.getOrNull()) + + /** + * The socket factory used to secure HTTPS connections. + * + * If this is set, then [trustManager] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + /** Alias for calling [Builder.sslSocketFactory] with `sslSocketFactory.orElse(null)`. */ + fun sslSocketFactory(sslSocketFactory: Optional) = + sslSocketFactory(sslSocketFactory.getOrNull()) + + /** + * The trust manager used to secure HTTPS connections. + * + * If this is set, then [sslSocketFactory] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + /** Alias for calling [Builder.trustManager] with `trustManager.orElse(null)`. */ + fun trustManager(trustManager: Optional) = + trustManager(trustManager.getOrNull()) + + /** + * The verifier used to confirm that response certificates apply to requested hostnames for + * HTTPS connections. + * + * If unset, then a default hostname verifier is used. + */ + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + /** Alias for calling [Builder.hostnameVerifier] with `hostnameVerifier.orElse(null)`. */ + fun hostnameVerifier(hostnameVerifier: Optional) = + hostnameVerifier(hostnameVerifier.getOrNull()) /** * Whether to throw an exception if any of the Jackson versions detected at runtime are @@ -195,7 +252,13 @@ class OpenAIOkHttpClientAsync private constructor() { OpenAIClientAsyncImpl( clientOptions .httpClient( - OkHttpClient.builder().timeout(clientOptions.timeout()).proxy(proxy).build() + OkHttpClient.builder() + .timeout(clientOptions.timeout()) + .proxy(proxy) + .sslSocketFactory(sslSocketFactory) + .trustManager(trustManager) + .hostnameVerifier(hostnameVerifier) + .build() ) .build() ) From b61c14dd2ef3b2932e7f7fc9b27bb3ae31abfe80 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:31:30 +0000 Subject: [PATCH 4/4] release: 2.17.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b2585653..c1a7e63f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.16.0" + ".": "2.17.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e2dab59..26c1f750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 2.17.0 (2025-07-21) + +Full Changelog: [v2.16.0...v2.17.0](https://github.com/openai/openai-java/compare/v2.16.0...v2.17.0) + +### Features + +* **client:** add https config options ([3f74519](https://github.com/openai/openai-java/commit/3f745195fa5d72ee72653adbd8087915c0db2fa5)) +* **client:** allow setting additional multipart body props ([220503e](https://github.com/openai/openai-java/commit/220503efc745bd1c74c02f1d4d6d3305198bf942)) + + +### Chores + +* **internal:** refactor delegating from client to options ([e89596d](https://github.com/openai/openai-java/commit/e89596d624fd77fdef750fb65a9ca5c6497e76cb)) + ## 2.16.0 (2025-07-17) Full Changelog: [v2.15.0...v2.16.0](https://github.com/openai/openai-java/compare/v2.15.0...v2.16.0) diff --git a/README.md b/README.md index 82b88c4e..3a908e5e 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/2.16.0) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/2.16.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/2.16.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/2.17.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/2.17.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/2.17.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/2.16.0). +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/2.17.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:2.16.0") +implementation("com.openai:openai-java:2.17.0") ``` ### Maven @@ -31,7 +31,7 @@ implementation("com.openai:openai-java:2.16.0") com.openai openai-java - 2.16.0 + 2.17.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 2fe350c2..b0887741 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "2.16.0" // x-release-please-version + version = "2.17.0" // x-release-please-version } subprojects {