diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a43b15a4..03276cfb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.11.0" + ".": "4.12.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index da4ec1f6..a6811613 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-41f98da99f44ebe6204fce5c1dc9940f85f3472779e797b674c4fdc20306c77d.yml -openapi_spec_hash: c61259027f421f501bdc6b23cf9e430e -config_hash: 141b101c9f13b90e21af74e1686f1f41 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fe8e67bdc351a518b113ab48e775750190e207807903d6b03ab22c438c38a588.yml +openapi_spec_hash: 8af972190647ffb9dcec516e19d8761a +config_hash: 856bee50ee3617e85a9bc9274db01dbb diff --git a/CHANGELOG.md b/CHANGELOG.md index 0752c1a4..5f1df44c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 4.12.0 (2025-12-15) + +Full Changelog: [v4.11.0...v4.12.0](https://github.com/openai/openai-java/compare/v4.11.0...v4.12.0) + +### Features + +* **api:** api update ([29174d7](https://github.com/openai/openai-java/commit/29174d7275d95ef5a6d4f88d4ebe2bcd178746b5)) +* **api:** fix grader input list, add dated slugs for sora-2 ([f75af83](https://github.com/openai/openai-java/commit/f75af830fdaf2874313c125a8de0aecde6f447b7)) + ## 4.11.0 (2025-12-12) Full Changelog: [v4.10.0...v4.11.0](https://github.com/openai/openai-java/compare/v4.10.0...v4.11.0) diff --git a/README.md b/README.md index 6136d625..91e42936 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/4.11.0) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/4.11.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/4.11.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/4.12.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/4.12.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/4.12.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/4.11.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/4.12.0). @@ -24,7 +24,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor ### Gradle ```kotlin -implementation("com.openai:openai-java:4.11.0") +implementation("com.openai:openai-java:4.12.0") ``` ### Maven @@ -33,7 +33,7 @@ implementation("com.openai:openai-java:4.11.0") com.openai openai-java - 4.11.0 + 4.12.0 ``` @@ -1342,7 +1342,7 @@ If you're using Spring Boot, then you can use the SDK's [Spring Boot starter](ht #### Gradle ```kotlin -implementation("com.openai:openai-java-spring-boot-starter:4.11.0") +implementation("com.openai:openai-java-spring-boot-starter:4.12.0") ``` #### Maven @@ -1351,7 +1351,7 @@ implementation("com.openai:openai-java-spring-boot-starter:4.11.0") com.openai openai-java-spring-boot-starter - 4.11.0 + 4.12.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 01745c5a..0aac8875 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "4.11.0" // x-release-please-version + version = "4.12.0" // x-release-please-version } subprojects { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt index 7807447f..1974483f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt @@ -29,6 +29,7 @@ import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.graders.gradermodels.PythonGrader import com.openai.models.graders.gradermodels.ScoreModelGrader import com.openai.models.graders.gradermodels.StringCheckGrader @@ -3239,7 +3240,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, output + * text, input images, and input audio, either as a single item or an array of + * items. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -3334,7 +3337,11 @@ private constructor( additionalProperties = evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output + * text, input images, and input audio, either as a single item or an array + * of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -3372,17 +3379,10 @@ private constructor( content(Content.ofResponseInputAudio(responseInputAudio)) /** - * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * Alias for calling [content] with `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, `system`, or @@ -3488,7 +3488,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output + * text, input images, and input audio, either as a single item or an array of + * items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -3498,8 +3502,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -3513,7 +3516,7 @@ private constructor( /** A text output from the model. */ fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) /** An audio input to the model. */ @@ -3521,11 +3524,11 @@ private constructor( Optional.ofNullable(responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input image, - * or input audio object. + * A list of inputs, each of which may be either an input text, output text, + * input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -3537,8 +3540,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -3550,7 +3552,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -3558,13 +3560,11 @@ private constructor( responseInputAudio.getOrThrow("responseInputAudio") /** - * A list of inputs, each of which may be either an input text, input image, - * or input audio object. + * A list of inputs, each of which may be either an input text, output text, + * input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -3577,10 +3577,7 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -3615,9 +3612,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -3657,9 +3656,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -3676,8 +3675,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -3687,7 +3685,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -3699,8 +3697,7 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -3721,7 +3718,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -3732,17 +3729,12 @@ private constructor( Content(responseInputAudio = responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input - * image, or input audio object. + * A list of inputs, each of which may be either an input text, output + * text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio.toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -3760,19 +3752,17 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ fun visitResponseInputAudio(responseInputAudio: ResponseInputAudio): T /** - * A list of inputs, each of which may be either an input text, input - * image, or input audio object. + * A list of inputs, each of which may be either an input text, output + * text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -3817,14 +3807,11 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { Content(textInput = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef>()) - ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) - }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(graderInputs = it, _json = json) }, ) .filterNotNull() .allMaxBy { it.validity() } @@ -3862,10 +3849,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") } @@ -4093,7 +4078,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -4362,6 +4347,860 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or input + * audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> visitor.visitOutputText(outputText) + inputImage != null -> visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + + override fun visitInputImage(inputImage: InputImage) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText(outputText: OutputText) = + outputText.validity() + + override fun visitInputImage(inputImage: InputImage) = + inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> "EvalContentItem{outputText=$outputText}" + inputImage != null -> "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> throw IllegalStateException("Invalid EvalContentItem") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText(responseInputText: ResponseInputText) = + EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** An image input block used within EvalItem content arrays. */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio(responseInputAudio: ResponseInputAudio) = + EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText(responseInputText: ResponseInputText): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** An image input block used within EvalItem content arrays. */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value of type + * [T]. + * + * An instance of [EvalContentItem] can contain an unknown variant + * if it was deserialized from data that doesn't match any known + * variant. For example, if the SDK is on an older version than the + * API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely + // incompatible with all the possible variants (e.g. + // deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, + // then use the first completely valid match, or simply the + // first match if none are completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid EvalContentItem") + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and logging + * (e.g. if the server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an instance of + * [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed + * [String] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and logging + * (e.g. if the server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. One of + * `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has an + * unexpected type (e.g. if the server responded with an + * unexpected value). + */ + fun detail(): Optional = detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an instance of + * [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a well-typed + * [String] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the model. One of + * `high`, `low`, or `auto`. Defaults to `auto`. + */ + fun detail(detail: String) = detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a well-typed + * [String] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(imageUrl, type, detail, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt index c010ad47..a202c89e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatJsonSchema @@ -2754,7 +2755,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, output + * text, input images, and input audio, either as a single item or an array of + * items. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -2849,7 +2852,11 @@ private constructor( additionalProperties = evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output + * text, input images, and input audio, either as a single item or an array + * of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -2887,17 +2894,10 @@ private constructor( content(Content.ofResponseInputAudio(responseInputAudio)) /** - * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * Alias for calling [content] with `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, `system`, or @@ -3003,7 +3003,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output + * text, input images, and input audio, either as a single item or an array of + * items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -3013,8 +3017,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -3028,7 +3031,7 @@ private constructor( /** A text output from the model. */ fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) /** An audio input to the model. */ @@ -3036,11 +3039,11 @@ private constructor( Optional.ofNullable(responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input image, - * or input audio object. + * A list of inputs, each of which may be either an input text, output text, + * input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -3052,8 +3055,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -3065,7 +3067,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -3073,13 +3075,11 @@ private constructor( responseInputAudio.getOrThrow("responseInputAudio") /** - * A list of inputs, each of which may be either an input text, input image, - * or input audio object. + * A list of inputs, each of which may be either an input text, output text, + * input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -3092,10 +3092,7 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -3130,9 +3127,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -3172,9 +3171,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -3191,8 +3190,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -3202,7 +3200,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -3214,8 +3212,7 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -3236,7 +3233,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -3247,17 +3244,12 @@ private constructor( Content(responseInputAudio = responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input - * image, or input audio object. + * A list of inputs, each of which may be either an input text, output + * text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio.toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -3275,19 +3267,17 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ fun visitResponseInputAudio(responseInputAudio: ResponseInputAudio): T /** - * A list of inputs, each of which may be either an input text, input - * image, or input audio object. + * A list of inputs, each of which may be either an input text, output + * text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -3332,14 +3322,11 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { Content(textInput = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef>()) - ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) - }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(graderInputs = it, _json = json) }, ) .filterNotNull() .allMaxBy { it.validity() } @@ -3377,10 +3364,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") } @@ -3608,7 +3593,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -3877,6 +3862,860 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or input + * audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> visitor.visitOutputText(outputText) + inputImage != null -> visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + + override fun visitInputImage(inputImage: InputImage) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText(outputText: OutputText) = + outputText.validity() + + override fun visitInputImage(inputImage: InputImage) = + inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> "EvalContentItem{outputText=$outputText}" + inputImage != null -> "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> throw IllegalStateException("Invalid EvalContentItem") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText(responseInputText: ResponseInputText) = + EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** An image input block used within EvalItem content arrays. */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio(responseInputAudio: ResponseInputAudio) = + EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText(responseInputText: ResponseInputText): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** An image input block used within EvalItem content arrays. */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value of type + * [T]. + * + * An instance of [EvalContentItem] can contain an unknown variant + * if it was deserialized from data that doesn't match any known + * variant. For example, if the SDK is on an older version than the + * API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely + // incompatible with all the possible variants (e.g. + // deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, + // then use the first completely valid match, or simply the + // first match if none are completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid EvalContentItem") + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and logging + * (e.g. if the server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an instance of + * [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed + * [String] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and logging + * (e.g. if the server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. One of + * `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has an + * unexpected type (e.g. if the server responded with an + * unexpected value). + */ + fun detail(): Optional = detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an instance of + * [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a well-typed + * [String] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the model. One of + * `high`, `low`, or `auto`. Defaults to `auto`. + */ + fun detail(detail: String) = detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a well-typed + * [String] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(imageUrl, type, detail, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt index f05d4645..a6af2f6b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatText @@ -4230,7 +4231,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. * * @throws OpenAIInvalidDataException if the JSON field has an * unexpected type or is unexpectedly missing or null (e.g. if the @@ -4332,7 +4335,11 @@ private constructor( evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports + * text, output text, input images, and input audio, either as a + * single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -4383,16 +4390,10 @@ private constructor( /** * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, @@ -4500,7 +4501,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -4510,9 +4515,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: - List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -4527,7 +4530,7 @@ private constructor( fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) @@ -4537,11 +4540,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): - Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -4553,8 +4555,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -4566,7 +4567,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -4575,12 +4576,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -4593,10 +4592,8 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> + visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -4631,10 +4628,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -4674,10 +4672,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -4694,8 +4691,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -4705,7 +4701,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -4717,8 +4713,8 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> + "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -4740,7 +4736,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -4753,17 +4749,11 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio - .toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -4783,7 +4773,7 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ @@ -4793,11 +4783,9 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -4864,14 +4852,10 @@ private constructor( }, tryDeserialize( node, - jacksonTypeRef>(), + jacksonTypeRef>(), ) ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -4913,11 +4897,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != - null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") @@ -5162,7 +5143,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -5447,6 +5428,942 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or + * input audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = + Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = + Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = + Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = + outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = + inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> + visitor.visitOutputText(outputText) + inputImage != null -> + visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText( + outputText: OutputText + ) { + outputText.validate() + } + + override fun visitInputImage( + inputImage: InputImage + ) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText( + outputText: OutputText + ) = outputText.validity() + + override fun visitInputImage( + inputImage: InputImage + ) = inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> + "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> + "EvalContentItem{outputText=$outputText}" + inputImage != null -> + "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText( + responseInputText: ResponseInputText + ) = EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** + * An image input block used within EvalItem content arrays. + */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText( + responseInputText: ResponseInputText + ): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** + * An image input block used within EvalItem content arrays. + */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value + * of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown + * variant if it was deserialized from data that doesn't + * match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default + * implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is + // completely incompatible with all the possible + // variants (e.g. deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest + // validity, then use the first completely valid + // match, or simply the first match if none are + // completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> + generator.writeObject(value._json) + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = + JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun text(text: JsonField) = apply { + this.text = text + } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 + else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. + * One of `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has + * an unexpected type (e.g. if the server responded with + * an unexpected value). + */ + fun detail(): Optional = + detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = + JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the + * model. One of `high`, `low`, or `auto`. Defaults to + * `auto`. + */ + fun detail(detail: String) = + detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 + else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + imageUrl, + type, + detail, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt index 263508bf..d0d62e4d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt @@ -29,6 +29,7 @@ import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatText @@ -4115,7 +4116,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. * * @throws OpenAIInvalidDataException if the JSON field has an * unexpected type or is unexpectedly missing or null (e.g. if the @@ -4217,7 +4220,11 @@ private constructor( evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports + * text, output text, input images, and input audio, either as a + * single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -4268,16 +4275,10 @@ private constructor( /** * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, @@ -4385,7 +4386,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -4395,9 +4400,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: - List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -4412,7 +4415,7 @@ private constructor( fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) @@ -4422,11 +4425,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): - Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -4438,8 +4440,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -4451,7 +4452,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -4460,12 +4461,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -4478,10 +4477,8 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> + visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -4516,10 +4513,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -4559,10 +4557,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -4579,8 +4576,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -4590,7 +4586,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -4602,8 +4598,8 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> + "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -4625,7 +4621,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -4638,17 +4634,11 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio - .toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -4668,7 +4658,7 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ @@ -4678,11 +4668,9 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -4749,14 +4737,10 @@ private constructor( }, tryDeserialize( node, - jacksonTypeRef>(), + jacksonTypeRef>(), ) ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -4798,11 +4782,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != - null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") @@ -5047,7 +5028,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -5332,6 +5313,942 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or + * input audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = + Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = + Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = + Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = + outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = + inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> + visitor.visitOutputText(outputText) + inputImage != null -> + visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText( + outputText: OutputText + ) { + outputText.validate() + } + + override fun visitInputImage( + inputImage: InputImage + ) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText( + outputText: OutputText + ) = outputText.validity() + + override fun visitInputImage( + inputImage: InputImage + ) = inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> + "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> + "EvalContentItem{outputText=$outputText}" + inputImage != null -> + "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText( + responseInputText: ResponseInputText + ) = EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** + * An image input block used within EvalItem content arrays. + */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText( + responseInputText: ResponseInputText + ): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** + * An image input block used within EvalItem content arrays. + */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value + * of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown + * variant if it was deserialized from data that doesn't + * match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default + * implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is + // completely incompatible with all the possible + // variants (e.g. deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest + // validity, then use the first completely valid + // match, or simply the first match if none are + // completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> + generator.writeObject(value._json) + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = + JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun text(text: JsonField) = apply { + this.text = text + } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 + else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. + * One of `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has + * an unexpected type (e.g. if the server responded with + * an unexpected value). + */ + fun detail(): Optional = + detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = + JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the + * model. One of `high`, `low`, or `auto`. Defaults to + * `auto`. + */ + fun detail(detail: String) = + detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 + else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + imageUrl, + type, + detail, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt index 589bd9a1..85a0acc4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatText @@ -4230,7 +4231,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. * * @throws OpenAIInvalidDataException if the JSON field has an * unexpected type or is unexpectedly missing or null (e.g. if the @@ -4332,7 +4335,11 @@ private constructor( evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports + * text, output text, input images, and input audio, either as a + * single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -4383,16 +4390,10 @@ private constructor( /** * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, @@ -4500,7 +4501,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -4510,9 +4515,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: - List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -4527,7 +4530,7 @@ private constructor( fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) @@ -4537,11 +4540,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): - Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -4553,8 +4555,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -4566,7 +4567,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -4575,12 +4576,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -4593,10 +4592,8 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> + visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -4631,10 +4628,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -4674,10 +4672,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -4694,8 +4691,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -4705,7 +4701,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -4717,8 +4713,8 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> + "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -4740,7 +4736,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -4753,17 +4749,11 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio - .toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -4783,7 +4773,7 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ @@ -4793,11 +4783,9 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -4864,14 +4852,10 @@ private constructor( }, tryDeserialize( node, - jacksonTypeRef>(), + jacksonTypeRef>(), ) ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -4913,11 +4897,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != - null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") @@ -5162,7 +5143,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -5447,6 +5428,942 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or + * input audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = + Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = + Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = + Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = + outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = + inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> + visitor.visitOutputText(outputText) + inputImage != null -> + visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText( + outputText: OutputText + ) { + outputText.validate() + } + + override fun visitInputImage( + inputImage: InputImage + ) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText( + outputText: OutputText + ) = outputText.validity() + + override fun visitInputImage( + inputImage: InputImage + ) = inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> + "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> + "EvalContentItem{outputText=$outputText}" + inputImage != null -> + "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText( + responseInputText: ResponseInputText + ) = EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** + * An image input block used within EvalItem content arrays. + */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText( + responseInputText: ResponseInputText + ): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** + * An image input block used within EvalItem content arrays. + */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value + * of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown + * variant if it was deserialized from data that doesn't + * match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default + * implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is + // completely incompatible with all the possible + // variants (e.g. deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest + // validity, then use the first completely valid + // match, or simply the first match if none are + // completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> + generator.writeObject(value._json) + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = + JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun text(text: JsonField) = apply { + this.text = text + } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 + else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. + * One of `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has + * an unexpected type (e.g. if the server responded with + * an unexpected value). + */ + fun detail(): Optional = + detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = + JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the + * model. One of `high`, `low`, or `auto`. Defaults to + * `auto`. + */ + fun detail(detail: String) = + detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 + else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + imageUrl, + type, + detail, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt index 6653a4d0..8c99b115 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatText @@ -4230,7 +4231,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. * * @throws OpenAIInvalidDataException if the JSON field has an * unexpected type or is unexpectedly missing or null (e.g. if the @@ -4332,7 +4335,11 @@ private constructor( evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports + * text, output text, input images, and input audio, either as a + * single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -4383,16 +4390,10 @@ private constructor( /** * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, @@ -4500,7 +4501,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -4510,9 +4515,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: - List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -4527,7 +4530,7 @@ private constructor( fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) @@ -4537,11 +4540,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): - Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -4553,8 +4555,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -4566,7 +4567,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -4575,12 +4576,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -4593,10 +4592,8 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> + visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -4631,10 +4628,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -4674,10 +4672,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -4694,8 +4691,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -4705,7 +4701,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -4717,8 +4713,8 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> + "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -4740,7 +4736,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -4753,17 +4749,11 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio - .toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -4783,7 +4773,7 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ @@ -4793,11 +4783,9 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -4864,14 +4852,10 @@ private constructor( }, tryDeserialize( node, - jacksonTypeRef>(), + jacksonTypeRef>(), ) ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -4913,11 +4897,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != - null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") @@ -5162,7 +5143,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -5447,6 +5428,942 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or + * input audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = + Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = + Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = + Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = + outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = + inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> + visitor.visitOutputText(outputText) + inputImage != null -> + visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText( + outputText: OutputText + ) { + outputText.validate() + } + + override fun visitInputImage( + inputImage: InputImage + ) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText( + outputText: OutputText + ) = outputText.validity() + + override fun visitInputImage( + inputImage: InputImage + ) = inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> + "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> + "EvalContentItem{outputText=$outputText}" + inputImage != null -> + "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText( + responseInputText: ResponseInputText + ) = EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** + * An image input block used within EvalItem content arrays. + */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText( + responseInputText: ResponseInputText + ): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** + * An image input block used within EvalItem content arrays. + */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value + * of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown + * variant if it was deserialized from data that doesn't + * match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default + * implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is + // completely incompatible with all the possible + // variants (e.g. deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest + // validity, then use the first completely valid + // match, or simply the first match if none are + // completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> + generator.writeObject(value._json) + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = + JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun text(text: JsonField) = apply { + this.text = text + } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 + else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. + * One of `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has + * an unexpected type (e.g. if the server responded with + * an unexpected value). + */ + fun detail(): Optional = + detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = + JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the + * model. One of `high`, `low`, or `auto`. Defaults to + * `auto`. + */ + fun detail(detail: String) = + detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 + else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + imageUrl, + type, + detail, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt index 81d9ff1d..caae91ca 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatText @@ -4230,7 +4231,9 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. * * @throws OpenAIInvalidDataException if the JSON field has an * unexpected type or is unexpectedly missing or null (e.g. if the @@ -4332,7 +4335,11 @@ private constructor( evalItem.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports + * text, output text, input images, and input audio, either as a + * single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -4383,16 +4390,10 @@ private constructor( /** * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. + * `Content.ofGraderInputs(graderInputs)`. */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, @@ -4500,7 +4501,11 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, + * output text, input images, and input audio, either as a single item + * or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -4510,9 +4515,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: - List? = - null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -4527,7 +4530,7 @@ private constructor( fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) @@ -4537,11 +4540,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): - Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = + Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -4553,8 +4555,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -4566,7 +4567,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -4575,12 +4576,10 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = + graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -4593,10 +4592,8 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> + visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -4631,10 +4628,11 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) {} + override fun visitGraderInputs( + graderInputs: List + ) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -4674,10 +4672,9 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: - List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs( + graderInputs: List + ) = graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -4694,8 +4691,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -4705,7 +4701,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -4717,8 +4713,8 @@ private constructor( inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> + "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -4740,7 +4736,7 @@ private constructor( fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -4753,17 +4749,11 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio - .toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -4783,7 +4773,7 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ @@ -4793,11 +4783,9 @@ private constructor( /** * A list of inputs, each of which may be either an input text, - * input image, or input audio object. + * output text, input image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -4864,14 +4852,10 @@ private constructor( }, tryDeserialize( node, - jacksonTypeRef>(), + jacksonTypeRef>(), ) ?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = - it, - _json = json, - ) + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -4913,11 +4897,8 @@ private constructor( generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != - null -> - generator.writeObject( - value.anArrayOfInputTextInputImageAndInputAudio - ) + value.graderInputs != null -> + generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") @@ -5162,7 +5143,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -5447,6 +5428,942 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** + * A single content item: input text, output text, input image, or + * input audio. + */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = + Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = + Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = + Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = + outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = + inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> + visitor.visitOutputText(outputText) + inputImage != null -> + visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText( + outputText: OutputText + ) { + outputText.validate() + } + + override fun visitInputImage( + inputImage: InputImage + ) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText( + outputText: OutputText + ) = outputText.validity() + + override fun visitInputImage( + inputImage: InputImage + ) = inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> + "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> + "EvalContentItem{outputText=$outputText}" + inputImage != null -> + "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = + EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText( + responseInputText: ResponseInputText + ) = EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** + * An image input block used within EvalItem content arrays. + */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of + * [EvalContentItem] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText( + responseInputText: ResponseInputText + ): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** + * An image input block used within EvalItem content arrays. + */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value + * of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown + * variant if it was deserialized from data that doesn't + * match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default + * implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException( + "Unknown EvalContentItem: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + outputText = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + inputImage = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + responseInputAudio = it, + _json = json, + ) + }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + EvalContentItem( + textInput = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is + // completely incompatible with all the possible + // variants (e.g. deserializing from array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest + // validity, then use the first completely valid + // match, or simply the first match if none are + // completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } + ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> + generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> + generator.writeObject(value.outputText) + value.inputImage != null -> + generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> + generator.writeObject(value._json) + else -> + throw IllegalStateException( + "Invalid EvalContentItem" + ) + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("text") + @ExcludeMissing + fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = + JsonValue.from("output_text") + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = + outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun text(text: JsonField) = apply { + this.text = text + } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("output_text")) 1 + else 0 + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: + MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and + * logging (e.g. if the server responded with an unexpected + * value). + */ + @JsonProperty("type") + @ExcludeMissing + fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. + * One of `high`, `low`, or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has + * an unexpected type (e.g. if the server responded with + * an unexpected value). + */ + fun detail(): Optional = + detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an + * instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = + JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: + MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = + inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = + imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the + * model. One of `high`, `low`, or `auto`. Defaults to + * `auto`. + */ + fun detail(detail: String) = + detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a + * well-typed [String] value instead. This method is + * primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun detail(detail: JsonField) = apply { + this.detail = detail + } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the + * returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field + * is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("input_image")) 1 + else 0 + } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + imageUrl, + type, + detail, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** diff --git a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt index c101f32f..48fad67e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.responses.ResponseInputAudio import com.openai.models.responses.ResponseInputText import java.util.Collections @@ -422,7 +423,8 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, output text, input + * images, and input audio, either as a single item or an array of items. * * @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). @@ -508,7 +510,10 @@ private constructor( additionalProperties = input.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output text, input + * images, and input audio, either as a single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -541,18 +546,9 @@ private constructor( fun content(responseInputAudio: ResponseInputAudio) = content(Content.ofResponseInputAudio(responseInputAudio)) - /** - * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. - */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + /** Alias for calling [content] with `Content.ofGraderInputs(graderInputs)`. */ + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, `system`, or `developer`. @@ -654,7 +650,10 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output text, input + * images, and input audio, either as a single item or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -664,7 +663,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: List? = null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -678,7 +677,7 @@ private constructor( /** A text output from the model. */ fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) /** An audio input to the model. */ @@ -686,11 +685,10 @@ private constructor( Optional.ofNullable(responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input image, or input - * audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -702,8 +700,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -715,7 +712,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -723,13 +720,10 @@ private constructor( responseInputAudio.getOrThrow("responseInputAudio") /** - * A list of inputs, each of which may be either an input text, input image, or input - * audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -741,10 +735,7 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -777,9 +768,9 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) {} + override fun visitGraderInputs(graderInputs: List) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -816,9 +807,8 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs(graderInputs: List) = + graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -835,8 +825,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -846,7 +835,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -856,8 +845,7 @@ private constructor( outputText != null -> "Content{outputText=$outputText}" inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -876,7 +864,7 @@ private constructor( @JvmStatic fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -886,17 +874,12 @@ private constructor( Content(responseInputAudio = responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input image, or - * input audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio.toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -914,19 +897,17 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ fun visitResponseInputAudio(responseInputAudio: ResponseInputAudio): T /** - * A list of inputs, each of which may be either an input text, input image, or - * input audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -965,11 +946,8 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { Content(textInput = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef>())?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = it, - _json = json, - ) + tryDeserialize(node, jacksonTypeRef>())?.let { + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -1003,8 +981,7 @@ private constructor( value.inputImage != null -> generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != null -> - generator.writeObject(value.anArrayOfInputTextInputImageAndInputAudio) + value.graderInputs != null -> generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") } @@ -1219,7 +1196,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -1472,6 +1449,783 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** A single content item: input text, output text, input image, or input audio. */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> visitor.visitOutputText(outputText) + inputImage != null -> visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + + override fun visitInputImage(inputImage: InputImage) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText(outputText: OutputText) = + outputText.validity() + + override fun visitInputImage(inputImage: InputImage) = + inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> "EvalContentItem{outputText=$outputText}" + inputImage != null -> "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> throw IllegalStateException("Invalid EvalContentItem") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText(responseInputText: ResponseInputText) = + EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** An image input block used within EvalItem content arrays. */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio(responseInputAudio: ResponseInputAudio) = + EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of [EvalContentItem] to a value + * of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText(responseInputText: ResponseInputText): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** An image input block used within EvalItem content arrays. */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio(responseInputAudio: ResponseInputAudio): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown EvalContentItem: $json") + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(responseInputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(outputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(inputImage = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem(responseInputAudio = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(textInput = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely + // incompatible with all the possible variants (e.g. deserializing from + // array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> generator.writeObject(value.outputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid EvalContentItem") + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("output_text") + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("output_text")) 1 else 0 } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. One of `high`, `low`, + * or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun detail(): Optional = detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the model. One of `high`, + * `low`, or `auto`. Defaults to `auto`. + */ + fun detail(detail: String) = detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun detail(detail: JsonField) = apply { this.detail = detail } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("input_image")) 1 else 0 } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(imageUrl, type, detail, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** The role of the message input. One of `user`, `assistant`, `system`, or `developer`. */ diff --git a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt index 67b34ab5..a9ad8ead 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt @@ -26,6 +26,7 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.EvalContentItem import com.openai.models.ReasoningEffort import com.openai.models.responses.ResponseInputAudio import com.openai.models.responses.ResponseInputText @@ -415,7 +416,8 @@ private constructor( ) : this(content, role, type, mutableMapOf()) /** - * Inputs to the model - can contain template strings. + * Inputs to the model - can contain template strings. Supports text, output text, input + * images, and input audio, either as a single item or an array of items. * * @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). @@ -501,7 +503,10 @@ private constructor( additionalProperties = input.additionalProperties.toMutableMap() } - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output text, input + * images, and input audio, either as a single item or an array of items. + */ fun content(content: Content) = content(JsonField.of(content)) /** @@ -534,18 +539,9 @@ private constructor( fun content(responseInputAudio: ResponseInputAudio) = content(Content.ofResponseInputAudio(responseInputAudio)) - /** - * Alias for calling [content] with - * `Content.ofAnArrayOfInputTextInputImageAndInputAudio(anArrayOfInputTextInputImageAndInputAudio)`. - */ - fun contentOfAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - content( - Content.ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) - ) + /** Alias for calling [content] with `Content.ofGraderInputs(graderInputs)`. */ + fun contentOfGraderInputs(graderInputs: List) = + content(Content.ofGraderInputs(graderInputs)) /** * The role of the message input. One of `user`, `assistant`, `system`, or `developer`. @@ -647,7 +643,10 @@ private constructor( (role.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) - /** Inputs to the model - can contain template strings. */ + /** + * Inputs to the model - can contain template strings. Supports text, output text, input + * images, and input audio, either as a single item or an array of items. + */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) class Content @@ -657,7 +656,7 @@ private constructor( private val outputText: OutputText? = null, private val inputImage: InputImage? = null, private val responseInputAudio: ResponseInputAudio? = null, - private val anArrayOfInputTextInputImageAndInputAudio: List? = null, + private val graderInputs: List? = null, private val _json: JsonValue? = null, ) { @@ -671,7 +670,7 @@ private constructor( /** A text output from the model. */ fun outputText(): Optional = Optional.ofNullable(outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun inputImage(): Optional = Optional.ofNullable(inputImage) /** An audio input to the model. */ @@ -679,11 +678,10 @@ private constructor( Optional.ofNullable(responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input image, or input - * audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ - fun anArrayOfInputTextInputImageAndInputAudio(): Optional> = - Optional.ofNullable(anArrayOfInputTextInputImageAndInputAudio) + fun graderInputs(): Optional> = Optional.ofNullable(graderInputs) fun isTextInput(): Boolean = textInput != null @@ -695,8 +693,7 @@ private constructor( fun isResponseInputAudio(): Boolean = responseInputAudio != null - fun isAnArrayOfInputTextInputImageAndInputAudio(): Boolean = - anArrayOfInputTextInputImageAndInputAudio != null + fun isGraderInputs(): Boolean = graderInputs != null /** A text input to the model. */ fun asTextInput(): String = textInput.getOrThrow("textInput") @@ -708,7 +705,7 @@ private constructor( /** A text output from the model. */ fun asOutputText(): OutputText = outputText.getOrThrow("outputText") - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") /** An audio input to the model. */ @@ -716,13 +713,10 @@ private constructor( responseInputAudio.getOrThrow("responseInputAudio") /** - * A list of inputs, each of which may be either an input text, input image, or input - * audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ - fun asAnArrayOfInputTextInputImageAndInputAudio(): List = - anArrayOfInputTextInputImageAndInputAudio.getOrThrow( - "anArrayOfInputTextInputImageAndInputAudio" - ) + fun asGraderInputs(): List = graderInputs.getOrThrow("graderInputs") fun _json(): Optional = Optional.ofNullable(_json) @@ -734,10 +728,7 @@ private constructor( inputImage != null -> visitor.visitInputImage(inputImage) responseInputAudio != null -> visitor.visitResponseInputAudio(responseInputAudio) - anArrayOfInputTextInputImageAndInputAudio != null -> - visitor.visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio - ) + graderInputs != null -> visitor.visitGraderInputs(graderInputs) else -> visitor.unknown(_json) } @@ -770,9 +761,9 @@ private constructor( responseInputAudio.validate() } - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) {} + override fun visitGraderInputs(graderInputs: List) { + graderInputs.forEach { it.validate() } + } } ) validated = true @@ -809,9 +800,8 @@ private constructor( responseInputAudio: ResponseInputAudio ) = responseInputAudio.validity() - override fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = anArrayOfInputTextInputImageAndInputAudio.size + override fun visitGraderInputs(graderInputs: List) = + graderInputs.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -828,8 +818,7 @@ private constructor( outputText == other.outputText && inputImage == other.inputImage && responseInputAudio == other.responseInputAudio && - anArrayOfInputTextInputImageAndInputAudio == - other.anArrayOfInputTextInputImageAndInputAudio + graderInputs == other.graderInputs } override fun hashCode(): Int = @@ -839,7 +828,7 @@ private constructor( outputText, inputImage, responseInputAudio, - anArrayOfInputTextInputImageAndInputAudio, + graderInputs, ) override fun toString(): String = @@ -849,8 +838,7 @@ private constructor( outputText != null -> "Content{outputText=$outputText}" inputImage != null -> "Content{inputImage=$inputImage}" responseInputAudio != null -> "Content{responseInputAudio=$responseInputAudio}" - anArrayOfInputTextInputImageAndInputAudio != null -> - "Content{anArrayOfInputTextInputImageAndInputAudio=$anArrayOfInputTextInputImageAndInputAudio}" + graderInputs != null -> "Content{graderInputs=$graderInputs}" _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } @@ -869,7 +857,7 @@ private constructor( @JvmStatic fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ @JvmStatic fun ofInputImage(inputImage: InputImage) = Content(inputImage = inputImage) @@ -879,17 +867,12 @@ private constructor( Content(responseInputAudio = responseInputAudio) /** - * A list of inputs, each of which may be either an input text, input image, or - * input audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ @JvmStatic - fun ofAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ) = - Content( - anArrayOfInputTextInputImageAndInputAudio = - anArrayOfInputTextInputImageAndInputAudio.toImmutable() - ) + fun ofGraderInputs(graderInputs: List) = + Content(graderInputs = graderInputs.toImmutable()) } /** @@ -907,19 +890,17 @@ private constructor( /** A text output from the model. */ fun visitOutputText(outputText: OutputText): T - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ fun visitInputImage(inputImage: InputImage): T /** An audio input to the model. */ fun visitResponseInputAudio(responseInputAudio: ResponseInputAudio): T /** - * A list of inputs, each of which may be either an input text, input image, or - * input audio object. + * A list of inputs, each of which may be either an input text, output text, input + * image, or input audio object. */ - fun visitAnArrayOfInputTextInputImageAndInputAudio( - anArrayOfInputTextInputImageAndInputAudio: List - ): T + fun visitGraderInputs(graderInputs: List): T /** * Maps an unknown variant of [Content] to a value of type [T]. @@ -958,11 +939,8 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { Content(textInput = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef>())?.let { - Content( - anArrayOfInputTextInputImageAndInputAudio = it, - _json = json, - ) + tryDeserialize(node, jacksonTypeRef>())?.let { + Content(graderInputs = it, _json = json) }, ) .filterNotNull() @@ -996,8 +974,7 @@ private constructor( value.inputImage != null -> generator.writeObject(value.inputImage) value.responseInputAudio != null -> generator.writeObject(value.responseInputAudio) - value.anArrayOfInputTextInputImageAndInputAudio != null -> - generator.writeObject(value.anArrayOfInputTextInputImageAndInputAudio) + value.graderInputs != null -> generator.writeObject(value.graderInputs) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Content") } @@ -1212,7 +1189,7 @@ private constructor( "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" } - /** An image input to the model. */ + /** An image input block used within EvalItem content arrays. */ class InputImage @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -1465,6 +1442,783 @@ private constructor( override fun toString() = "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" } + + /** A single content item: input text, output text, input image, or input audio. */ + @JsonDeserialize(using = EvalContentItem.Deserializer::class) + @JsonSerialize(using = EvalContentItem.Serializer::class) + class EvalContentItem + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val inputImage: InputImage? = null, + private val responseInputAudio: ResponseInputAudio? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = Optional.ofNullable(outputText) + + /** An image input block used within EvalItem content arrays. */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** An audio input to the model. */ + fun responseInputAudio(): Optional = + Optional.ofNullable(responseInputAudio) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isResponseInputAudio(): Boolean = responseInputAudio != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + /** An image input block used within EvalItem content arrays. */ + fun asInputImage(): InputImage = inputImage.getOrThrow("inputImage") + + /** An audio input to the model. */ + fun asResponseInputAudio(): ResponseInputAudio = + responseInputAudio.getOrThrow("responseInputAudio") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> visitor.visitOutputText(outputText) + inputImage != null -> visitor.visitInputImage(inputImage) + responseInputAudio != null -> + visitor.visitResponseInputAudio(responseInputAudio) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalContentItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + + override fun visitInputImage(inputImage: InputImage) { + inputImage.validate() + } + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) { + responseInputAudio.validate() + } + } + ) + 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 = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText(outputText: OutputText) = + outputText.validity() + + override fun visitInputImage(inputImage: InputImage) = + inputImage.validity() + + override fun visitResponseInputAudio( + responseInputAudio: ResponseInputAudio + ) = responseInputAudio.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EvalContentItem && + textInput == other.textInput && + responseInputText == other.responseInputText && + outputText == other.outputText && + inputImage == other.inputImage && + responseInputAudio == other.responseInputAudio + } + + override fun hashCode(): Int = + Objects.hash( + textInput, + responseInputText, + outputText, + inputImage, + responseInputAudio, + ) + + override fun toString(): String = + when { + textInput != null -> "EvalContentItem{textInput=$textInput}" + responseInputText != null -> + "EvalContentItem{responseInputText=$responseInputText}" + outputText != null -> "EvalContentItem{outputText=$outputText}" + inputImage != null -> "EvalContentItem{inputImage=$inputImage}" + responseInputAudio != null -> + "EvalContentItem{responseInputAudio=$responseInputAudio}" + _json != null -> "EvalContentItem{_unknown=$_json}" + else -> throw IllegalStateException("Invalid EvalContentItem") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = EvalContentItem(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText(responseInputText: ResponseInputText) = + EvalContentItem(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + EvalContentItem(outputText = outputText) + + /** An image input block used within EvalItem content arrays. */ + @JvmStatic + fun ofInputImage(inputImage: InputImage) = + EvalContentItem(inputImage = inputImage) + + /** An audio input to the model. */ + @JvmStatic + fun ofResponseInputAudio(responseInputAudio: ResponseInputAudio) = + EvalContentItem(responseInputAudio = responseInputAudio) + } + + /** + * An interface that defines how to map each variant of [EvalContentItem] to a value + * of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText(responseInputText: ResponseInputText): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** An image input block used within EvalItem content arrays. */ + fun visitInputImage(inputImage: InputImage): T + + /** An audio input to the model. */ + fun visitResponseInputAudio(responseInputAudio: ResponseInputAudio): T + + /** + * Maps an unknown variant of [EvalContentItem] to a value of type [T]. + * + * An instance of [EvalContentItem] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown EvalContentItem: $json") + } + } + + internal class Deserializer : + BaseDeserializer(EvalContentItem::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): EvalContentItem { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(responseInputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(outputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(inputImage = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + EvalContentItem(responseInputAudio = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalContentItem(textInput = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely + // incompatible with all the possible variants (e.g. deserializing from + // array). + 0 -> EvalContentItem(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(EvalContentItem::class) { + + override fun serialize( + value: EvalContentItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> generator.writeObject(value.outputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value.responseInputAudio != null -> + generator.writeObject(value.responseInputAudio) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid EvalContentItem") + } + } + } + + /** A text output from the model. */ + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @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 text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @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 { + + /** + * Returns a mutable builder for constructing an instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("output_text") + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * 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("output_text") + * ``` + * + * 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 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 [OutputText]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + 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 (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("output_text")) 1 else 0 } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + /** An image input block used within EvalItem content arrays. */ + class InputImage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") + @ExcludeMissing + imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") + @ExcludeMissing + detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * The URL of the image input. + * + * @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 imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * The type of the image input. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. One of `high`, `low`, + * or `auto`. Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun detail(): Optional = detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("image_url") + @ExcludeMissing + fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("detail") + @ExcludeMissing + fun _detail(): JsonField = detail + + @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 { + + /** + * Returns a mutable builder for constructing an instance of [InputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(inputImage: InputImage) = apply { + imageUrl = inputImage.imageUrl + type = inputImage.type + detail = inputImage.detail + additionalProperties = inputImage.additionalProperties.toMutableMap() + } + + /** The URL of the image input. */ + fun imageUrl(imageUrl: String) = imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { + this.imageUrl = imageUrl + } + + /** + * 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("input_image") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the model. One of `high`, + * `low`, or `auto`. Defaults to `auto`. + */ + fun detail(detail: String) = detail(JsonField.of(detail)) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun detail(detail: JsonField) = apply { this.detail = detail } + + 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 [InputImage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InputImage = + InputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + detail() + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("input_image")) 1 else 0 } + + (if (detail.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(imageUrl, type, detail, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" + } + } } /** The role of the message input. One of `user`, `assistant`, `system`, or `developer`. */ diff --git a/openai-java-core/src/main/kotlin/com/openai/models/videos/VideoModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/videos/VideoModel.kt index 084bfe17..c20fd7b2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/videos/VideoModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/videos/VideoModel.kt @@ -24,6 +24,12 @@ class VideoModel @JsonCreator private constructor(private val value: JsonField Value.SORA_2 SORA_2_PRO -> Value.SORA_2_PRO + SORA_2_2025_10_06 -> Value.SORA_2_2025_10_06 + SORA_2_PRO_2025_10_06 -> Value.SORA_2_PRO_2025_10_06 + SORA_2_2025_12_08 -> Value.SORA_2_2025_12_08 else -> Value._UNKNOWN } @@ -75,6 +90,9 @@ class VideoModel @JsonCreator private constructor(private val value: JsonField Known.SORA_2 SORA_2_PRO -> Known.SORA_2_PRO + SORA_2_2025_10_06 -> Known.SORA_2_2025_10_06 + SORA_2_PRO_2025_10_06 -> Known.SORA_2_PRO_2025_10_06 + SORA_2_2025_12_08 -> Known.SORA_2_2025_12_08 else -> throw OpenAIInvalidDataException("Unknown VideoModel: $value") }