diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2adb41ca8..52afe059d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.41.1" + ".": "0.42.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index b21d5dae7..1f1a1736b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 80 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4bce8217a697c729ac98046d4caf2c9e826b54c427fb0ab4f98e549a2e0ce31c.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 2daae06cc598821ccf87201de0861e40 +config_hash: 178ba1bfb1237bf6b94abb3408072aa7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f53c2fbd..d05d1214e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 0.42.0 (2025-04-02) + +Full Changelog: [v0.41.1...v0.42.0](https://github.com/openai/openai-java/compare/v0.41.1...v0.42.0) + +### Features + +* **api:** manual updates ([0440105](https://github.com/openai/openai-java/commit/0440105162ce52cd187ce8ab8d5d1dc20682a4ed)) +* **client:** add enum validation method ([a641218](https://github.com/openai/openai-java/commit/a6412183ccea1ac3e9105793372693d5ec8c6d72)) +* **client:** make union deserialization more robust ([#385](https://github.com/openai/openai-java/issues/385)) ([a641218](https://github.com/openai/openai-java/commit/a6412183ccea1ac3e9105793372693d5ec8c6d72)) + + +### Chores + +* **client:** remove unnecessary json state from some query param classes ([a641218](https://github.com/openai/openai-java/commit/a6412183ccea1ac3e9105793372693d5ec8c6d72)) +* **internal:** add invalid json deserialization tests ([a641218](https://github.com/openai/openai-java/commit/a6412183ccea1ac3e9105793372693d5ec8c6d72)) +* **internal:** add json roundtripping tests ([a641218](https://github.com/openai/openai-java/commit/a6412183ccea1ac3e9105793372693d5ec8c6d72)) + ## 0.41.1 (2025-04-01) Full Changelog: [v0.41.0...v0.41.1](https://github.com/openai/openai-java/compare/v0.41.0...v0.41.1) diff --git a/README.md b/README.md index 898c552f0..f1d9f7ba3 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.41.1) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.41.1/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.41.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.42.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.42.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.42.0) @@ -18,7 +18,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 also available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/0.41.1). +The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are also available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/0.42.0). @@ -29,7 +29,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor ### Gradle ```kotlin -implementation("com.openai:openai-java:0.41.1") +implementation("com.openai:openai-java:0.42.0") ``` ### Maven @@ -38,7 +38,7 @@ implementation("com.openai:openai-java:0.41.1") com.openai openai-java - 0.41.1 + 0.42.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 00332ab66..12705605e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "0.41.1" // x-release-please-version + version = "0.42.0" // x-release-please-version } subprojects { diff --git a/openai-java-core/src/main/kotlin/com/openai/core/BaseDeserializer.kt b/openai-java-core/src/main/kotlin/com/openai/core/BaseDeserializer.kt index d30fb36da..c6dc038b4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/BaseDeserializer.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/BaseDeserializer.kt @@ -7,11 +7,9 @@ import com.fasterxml.jackson.databind.BeanProperty import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JavaType import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonMappingException import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.ContextualDeserializer import com.fasterxml.jackson.databind.deser.std.StdDeserializer -import com.openai.errors.OpenAIInvalidDataException import kotlin.reflect.KClass abstract class BaseDeserializer(type: KClass) : @@ -30,38 +28,17 @@ abstract class BaseDeserializer(type: KClass) : protected abstract fun ObjectCodec.deserialize(node: JsonNode): T - protected fun ObjectCodec.deserialize(node: JsonNode, type: TypeReference): T = + protected fun ObjectCodec.tryDeserialize(node: JsonNode, type: TypeReference): T? = try { readValue(treeAsTokens(node), type) } catch (e: Exception) { - throw OpenAIInvalidDataException("Error deserializing", e) - } - - protected fun ObjectCodec.tryDeserialize( - node: JsonNode, - type: TypeReference, - validate: (T) -> Unit = {}, - ): T? { - return try { - readValue(treeAsTokens(node), type).apply(validate) - } catch (e: JsonMappingException) { - null - } catch (e: RuntimeException) { null } - } - protected fun ObjectCodec.tryDeserialize( - node: JsonNode, - type: JavaType, - validate: (T) -> Unit = {}, - ): T? { - return try { - readValue(treeAsTokens(node), type).apply(validate) - } catch (e: JsonMappingException) { - null - } catch (e: RuntimeException) { + protected fun ObjectCodec.tryDeserialize(node: JsonNode, type: JavaType): T? = + try { + readValue(treeAsTokens(node), type) + } catch (e: Exception) { null } - } } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/Utils.kt b/openai-java-core/src/main/kotlin/com/openai/core/Utils.kt index a63ef98b6..0b11077b0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/Utils.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/Utils.kt @@ -25,6 +25,34 @@ internal fun , V> SortedMap.toImmutable(): SortedMap> Sequence.allMaxBy(selector: (T) -> R): List { + var maxValue: R? = null + val maxElements = mutableListOf() + + val iterator = iterator() + while (iterator.hasNext()) { + val element = iterator.next() + val value = selector(element) + if (maxValue == null || value > maxValue) { + maxValue = value + maxElements.clear() + maxElements.add(element) + } else if (value == maxValue) { + maxElements.add(element) + } + } + + return maxElements +} + /** * Returns whether [this] is equal to [other]. * diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AllModels.kt b/openai-java-core/src/main/kotlin/com/openai/models/AllModels.kt index 99061d321..b9077106b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/AllModels.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/AllModels.kt @@ -15,6 +15,7 @@ import com.openai.core.BaseSerializer import com.openai.core.Enum import com.openai.core.JsonField import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -50,14 +51,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) chatModel != null -> visitor.visitChatModel(chatModel) unionMember2 != null -> visitor.visitUnionMember2(unionMember2) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -70,14 +70,45 @@ private constructor( object : Visitor { override fun visitString(string: String) {} - override fun visitChatModel(chatModel: ChatModel) {} + override fun visitChatModel(chatModel: ChatModel) { + chatModel.validate() + } - override fun visitUnionMember2(unionMember2: UnionMember2) {} + override fun visitUnionMember2(unionMember2: UnionMember2) { + unionMember2.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 visitString(string: String) = 1 + + override fun visitChatModel(chatModel: ChatModel) = chatModel.validity() + + override fun visitUnionMember2(unionMember2: UnionMember2) = unionMember2.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -135,17 +166,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): AllModels { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return AllModels(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return AllModels(chatModel = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return AllModels(unionMember2 = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + AllModels(chatModel = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AllModels(unionMember2 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AllModels(string = 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 object). + 0 -> AllModels(_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() } - - return AllModels(_json = json) } } @@ -266,6 +310,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): UnionMember2 = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt index 81584236b..3db434916 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt @@ -338,6 +338,32 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) number != null -> visitor.visitNumber(number) bool != null -> visitor.visitBool(bool) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -440,6 +487,34 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitNumber(number: Double) = 1 + + override fun visitBool(bool: Boolean) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -497,17 +572,31 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Value { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Value(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return Value(number = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Value(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Value(number = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Value(bool = 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 object). + 0 -> Value(_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() } - tryDeserialize(node, jacksonTypeRef())?.let { - return Value(bool = it, _json = json) - } - - return Value(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/CompoundFilter.kt b/openai-java-core/src/main/kotlin/com/openai/models/CompoundFilter.kt index 19825efa6..6a33b4972 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/CompoundFilter.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/CompoundFilter.kt @@ -20,6 +20,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -28,6 +29,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Combine multiple filters using `and` or `or`. */ class CompoundFilter @@ -206,10 +208,28 @@ private constructor( } filters().forEach { it.validate() } - type() + type().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 = + (filters.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** * A filter used to compare a specified attribute key to a given value using a defined * comparison operation. @@ -245,13 +265,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { comparison != null -> visitor.visitComparison(comparison) jsonValue != null -> visitor.visitJsonValue(jsonValue) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -272,6 +291,33 @@ private constructor( 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 visitComparison(comparison: ComparisonFilter) = + comparison.validity() + + override fun visitJsonValue(jsonValue: JsonValue) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -333,15 +379,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Filter { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Filter(comparison = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return Filter(jsonValue = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Filter(comparison = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Filter(jsonValue = 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. + 0 -> Filter(_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() } - - return Filter(_json = json) } } @@ -448,6 +507,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ErrorObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/ErrorObject.kt index 0160fae8c..25d94a0e8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ErrorObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ErrorObject.kt @@ -236,6 +236,26 @@ private constructor( 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 (code.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + + (if (param.asKnown().isPresent) 1 else 0) + + (if (type.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FunctionDefinition.kt b/openai-java-core/src/main/kotlin/com/openai/models/FunctionDefinition.kt index 4b4e7d7b7..f0d56969e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/FunctionDefinition.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/FunctionDefinition.kt @@ -286,6 +286,26 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + + (if (description.asKnown().isPresent) 1 else 0) + + (parameters.asKnown().getOrNull()?.validity() ?: 0) + + (if (strict.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FunctionParameters.kt b/openai-java-core/src/main/kotlin/com/openai/models/FunctionParameters.kt index d78babcfe..f80f48f18 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/FunctionParameters.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/FunctionParameters.kt @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.ExcludeMissing import com.openai.core.JsonValue import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException import java.util.Objects /** @@ -83,6 +84,23 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Reasoning.kt b/openai-java-core/src/main/kotlin/com/openai/models/Reasoning.kt index 5028a1257..63f75e5bc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Reasoning.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Reasoning.kt @@ -193,11 +193,29 @@ private constructor( return@apply } - effort() - generateSummary() + effort().ifPresent { it.validate() } + generateSummary().ifPresent { it.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 = + (effort.asKnown().getOrNull()?.validity() ?: 0) + + (generateSummary.asKnown().getOrNull()?.validity() ?: 0) + /** * **computer_use_preview only** * @@ -293,6 +311,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): GenerateSummary = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ReasoningEffort.kt b/openai-java-core/src/main/kotlin/com/openai/models/ReasoningEffort.kt index 0c3fb2848..f0b62aa50 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ReasoningEffort.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ReasoningEffort.kt @@ -107,6 +107,32 @@ class ReasoningEffort @JsonCreator private constructor(private val value: JsonFi fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ReasoningEffort = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonObject.kt index 7570bffff..99b9814de 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonObject.kt @@ -129,6 +129,22 @@ private constructor( 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 = type.let { if (it == JsonValue.from("json_object")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt b/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt index 6584c82d9..9b8052424 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt @@ -187,6 +187,24 @@ private constructor( 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 = + (jsonSchema.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("json_schema")) 1 else 0 } + /** Structured Outputs configuration options, including a JSON Schema. */ class JsonSchema private constructor( @@ -448,6 +466,27 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + + (if (description.asKnown().isPresent) 1 else 0) + + (schema.asKnown().getOrNull()?.validity() ?: 0) + + (if (strict.asKnown().isPresent) 1 else 0) + /** * The schema for the response format, described as a JSON Schema object. Learn how to build * JSON schemas [here](https://json-schema.org/). @@ -521,6 +560,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatText.kt b/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatText.kt index 099bf9a5c..7daa00978 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatText.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatText.kt @@ -125,6 +125,22 @@ private constructor( 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 = type.let { if (it == JsonValue.from("text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ResponsesModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/ResponsesModel.kt index 3620dd701..c84795566 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ResponsesModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ResponsesModel.kt @@ -15,6 +15,7 @@ import com.openai.core.BaseSerializer import com.openai.core.Enum import com.openai.core.JsonField import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -50,14 +51,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) chat != null -> visitor.visitChat(chat) unionMember2 != null -> visitor.visitUnionMember2(unionMember2) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -70,14 +70,45 @@ private constructor( object : Visitor { override fun visitString(string: String) {} - override fun visitChat(chat: ChatModel) {} + override fun visitChat(chat: ChatModel) { + chat.validate() + } - override fun visitUnionMember2(unionMember2: UnionMember2) {} + override fun visitUnionMember2(unionMember2: UnionMember2) { + unionMember2.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 visitString(string: String) = 1 + + override fun visitChat(chat: ChatModel) = chat.validity() + + override fun visitUnionMember2(unionMember2: UnionMember2) = unionMember2.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -138,17 +169,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): ResponsesModel { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return ResponsesModel(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return ResponsesModel(chat = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ResponsesModel(chat = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponsesModel(unionMember2 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponsesModel(string = 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 object). + 0 -> ResponsesModel(_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() } - tryDeserialize(node, jacksonTypeRef())?.let { - return ResponsesModel(unionMember2 = it, _json = json) - } - - return ResponsesModel(_json = json) } } @@ -269,6 +313,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): UnionMember2 = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/AudioModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/AudioModel.kt index 8f06688a0..5f80b466d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/AudioModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/AudioModel.kt @@ -96,6 +96,32 @@ class AudioModel @JsonCreator private constructor(private val value: JsonField, @@ -389,6 +408,26 @@ private constructor( 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 (token.asKnown().isPresent) 1 else 0) + + (bytes.asKnown().getOrNull()?.size ?: 0) + + (if (logprob.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt index 49f092ede..5ff32133d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt @@ -960,16 +960,24 @@ private constructor( } file() - model() - include() + model().validate() + include().ifPresent { it.forEach { it.validate() } } language() prompt() - responseFormat() + responseFormat().ifPresent { it.validate() } temperature() - timestampGranularities() + timestampGranularities().ifPresent { it.forEach { it.validate() } } validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1079,6 +1087,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): TimestampGranularity = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponse.kt index d739c6749..1e3c0c4fa 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponse.kt @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.BaseDeserializer import com.openai.core.BaseSerializer import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -51,13 +52,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { transcription != null -> visitor.visitTranscription(transcription) verbose != null -> visitor.visitVerbose(verbose) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -80,6 +80,32 @@ private constructor( 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 visitTranscription(transcription: Transcription) = + transcription.validity() + + override fun visitVerbose(verbose: TranscriptionVerbose) = verbose.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -150,16 +176,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): TranscriptionCreateResponse { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return TranscriptionCreateResponse(transcription = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return TranscriptionCreateResponse(verbose = it, _json = json) - } - - return TranscriptionCreateResponse(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TranscriptionCreateResponse(transcription = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TranscriptionCreateResponse(verbose = 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 boolean). + 0 -> TranscriptionCreateResponse(_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() + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionInclude.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionInclude.kt index fb3e65689..2b15a3685 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionInclude.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionInclude.kt @@ -88,6 +88,32 @@ class TranscriptionInclude @JsonCreator private constructor(private val value: J fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): TranscriptionInclude = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt index 6d846bc9b..ffd5f56d7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt @@ -16,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class TranscriptionSegment private constructor( @@ -506,6 +507,32 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (avgLogprob.asKnown().isPresent) 1 else 0) + + (if (compressionRatio.asKnown().isPresent) 1 else 0) + + (if (end.asKnown().isPresent) 1 else 0) + + (if (noSpeechProb.asKnown().isPresent) 1 else 0) + + (if (seek.asKnown().isPresent) 1 else 0) + + (if (start.asKnown().isPresent) 1 else 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + + (tokens.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEvent.kt index 7ce4db533..5082a8439 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEvent.kt @@ -75,13 +75,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { transcriptTextDelta != null -> visitor.visitTranscriptTextDelta(transcriptTextDelta) transcriptTextDone != null -> visitor.visitTranscriptTextDone(transcriptTextDone) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -108,6 +107,35 @@ private constructor( 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 visitTranscriptTextDelta( + transcriptTextDelta: TranscriptionTextDeltaEvent + ) = transcriptTextDelta.validity() + + override fun visitTranscriptTextDone( + transcriptTextDone: TranscriptionTextDoneEvent + ) = transcriptTextDone.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -197,18 +225,14 @@ private constructor( when (type) { "transcript.text.delta" -> { - return TranscriptionStreamEvent( - transcriptTextDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { TranscriptionStreamEvent(transcriptTextDelta = it, _json = json) } + ?: TranscriptionStreamEvent(_json = json) } "transcript.text.done" -> { - return TranscriptionStreamEvent( - transcriptTextDone = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + TranscriptionStreamEvent(transcriptTextDone = it, _json = json) + } ?: TranscriptionStreamEvent(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEvent.kt index 6ceb607c0..0347259ff 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEvent.kt @@ -17,6 +17,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Emitted when there is an additional text delta. This is also the first event emitted when the @@ -239,6 +240,25 @@ private constructor( 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 (delta.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("transcript.text.delta")) 1 else 0 } + + (logprobs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class Logprob private constructor( private val token: JsonField, @@ -431,6 +451,26 @@ private constructor( 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 (token.asKnown().isPresent) 1 else 0) + + (bytes.asKnown().getOrNull()?.size ?: 0) + + (if (logprob.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEvent.kt index fde69760c..be812ec52 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEvent.kt @@ -17,6 +17,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Emitted when the transcription is complete. Contains the complete transcription text. Only @@ -239,6 +240,25 @@ private constructor( 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("transcript.text.done")) 1 else 0 } + + (logprobs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class Logprob private constructor( private val token: JsonField, @@ -431,6 +451,26 @@ private constructor( 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 (token.asKnown().isPresent) 1 else 0) + + (bytes.asKnown().getOrNull()?.size ?: 0) + + (if (logprob.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerbose.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerbose.kt index d5000b2db..80c584207 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerbose.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerbose.kt @@ -17,6 +17,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Represents a verbose json transcription response returned by model, based on the provided input. @@ -311,6 +312,27 @@ private constructor( 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 (duration.asKnown().isPresent) 1 else 0) + + (if (language.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + + (segments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (words.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt index 994383066..e17f25058 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt @@ -206,6 +206,25 @@ private constructor( 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 (end.asKnown().isPresent) 1 else 0) + + (if (start.asKnown().isPresent) 1 else 0) + + (if (word.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/Translation.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/Translation.kt index 1f620f5f0..9936b6440 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/Translation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/Translation.kt @@ -132,6 +132,21 @@ private constructor( 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) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt index 70ddd99d7..dc7565f58 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateParams.kt @@ -664,13 +664,21 @@ private constructor( } file() - model() + model().validate() prompt() - responseFormat() + responseFormat().ifPresent { it.validate() } temperature() validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -800,6 +808,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ResponseFormat = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateResponse.kt index f4851777f..16d1bb45f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationCreateResponse.kt @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.BaseDeserializer import com.openai.core.BaseSerializer import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -40,13 +41,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { translation != null -> visitor.visitTranslation(translation) verbose != null -> visitor.visitVerbose(verbose) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -69,6 +69,31 @@ private constructor( 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 visitTranslation(translation: Translation) = translation.validity() + + override fun visitVerbose(verbose: TranslationVerbose) = verbose.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -128,16 +153,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): TranslationCreateResponse { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return TranslationCreateResponse(translation = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return TranslationCreateResponse(verbose = it, _json = json) - } - - return TranslationCreateResponse(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TranslationCreateResponse(translation = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TranslationCreateResponse(verbose = 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 boolean). + 0 -> TranslationCreateResponse(_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() + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationVerbose.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationVerbose.kt index 8163dda1a..d39dec566 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationVerbose.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/translations/TranslationVerbose.kt @@ -18,6 +18,7 @@ import com.openai.models.audio.transcriptions.TranscriptionSegment import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class TranslationVerbose private constructor( @@ -262,6 +263,26 @@ private constructor( 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 (duration.asKnown().isPresent) 1 else 0) + + (if (language.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + + (segments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt index 79fad7cc1..8e52d00dd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt @@ -842,7 +842,7 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - status() + status().validate() cancelledAt() cancellingAt() completedAt() @@ -859,6 +859,42 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (completionWindow.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (endpoint.asKnown().isPresent) 1 else 0) + + (if (inputFileId.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("batch")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (cancelledAt.asKnown().isPresent) 1 else 0) + + (if (cancellingAt.asKnown().isPresent) 1 else 0) + + (if (completedAt.asKnown().isPresent) 1 else 0) + + (if (errorFileId.asKnown().isPresent) 1 else 0) + + (errors.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiredAt.asKnown().isPresent) 1 else 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (failedAt.asKnown().isPresent) 1 else 0) + + (if (finalizingAt.asKnown().isPresent) 1 else 0) + + (if (inProgressAt.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputFileId.asKnown().isPresent) 1 else 0) + + (requestCounts.asKnown().getOrNull()?.validity() ?: 0) + /** The current status of the batch. */ class Status @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -981,6 +1017,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1150,6 +1213,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (object_.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1242,6 +1324,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCreateParams.kt index abf1570ff..f03e6ec70 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCreateParams.kt @@ -652,13 +652,34 @@ private constructor( return@apply } - completionWindow() - endpoint() + completionWindow().validate() + endpoint().validate() inputFileId() metadata().ifPresent { it.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 = + (completionWindow.asKnown().getOrNull()?.validity() ?: 0) + + (endpoint.asKnown().getOrNull()?.validity() ?: 0) + + (if (inputFileId.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -763,6 +784,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): CompletionWindow = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -879,6 +927,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Endpoint = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -966,6 +1041,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchError.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchError.kt index bb39a74b4..ce543992a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchError.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchError.kt @@ -228,6 +228,26 @@ private constructor( 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 (code.asKnown().isPresent) 1 else 0) + + (if (line.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + + (if (param.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt index ed757ab48..3a82bac4d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.BatchService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt index d4f6fcc2f..e521f3e3e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.BatchServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRequestCounts.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRequestCounts.kt index 1bfafacc3..87d8cafec 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRequestCounts.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRequestCounts.kt @@ -207,6 +207,25 @@ private constructor( 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 (completed.asKnown().isPresent) 1 else 0) + + (if (failed.asKnown().isPresent) 1 else 0) + + (if (total.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/Assistant.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/Assistant.kt index d6ef22949..48e15613b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/Assistant.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/Assistant.kt @@ -789,6 +789,35 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (description.asKnown().isPresent) 1 else 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("assistant")) 1 else 0 } + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing * additional information about the object in a structured format, and querying for objects via @@ -863,6 +892,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1034,6 +1081,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -1172,6 +1238,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1332,6 +1414,23 @@ private constructor( 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 = (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantCreateParams.kt index 4d7cda26e..dc3ecf128 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantCreateParams.kt @@ -1466,12 +1466,12 @@ private constructor( return@apply } - model() + model().validate() description() instructions() metadata().ifPresent { it.validate() } name() - reasoningEffort() + reasoningEffort().ifPresent { it.validate() } responseFormat().ifPresent { it.validate() } temperature() toolResources().ifPresent { it.validate() } @@ -1480,6 +1480,34 @@ private constructor( 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 = + (model.asKnown().getOrNull()?.validity() ?: 0) + + (if (description.asKnown().isPresent) 1 else 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1572,6 +1600,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1743,6 +1789,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -1881,6 +1946,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2103,6 +2184,25 @@ private constructor( 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 = + (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + + (vectorStores.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class VectorStore private constructor( private val chunkingStrategy: JsonField, @@ -2364,6 +2464,26 @@ private constructor( 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 = + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (fileIds.asKnown().getOrNull()?.size ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + /** * The chunking strategy used to chunk the file(s). If not set, will use the `auto` * strategy. @@ -2399,13 +2519,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) static_ != null -> visitor.visitStatic(static_) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2434,6 +2553,35 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { + if (it == JsonValue.from(mapOf("type" to "auto"))) 1 else 0 + } + + override fun visitStatic(static_: StaticObject) = static_.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2505,16 +2653,14 @@ private constructor( when (type) { "auto" -> { - return ChunkingStrategy( - auto = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(auto = it, _json = json) } + ?.takeIf { it.isValid() } ?: ChunkingStrategy(_json = json) } "static" -> { - return ChunkingStrategy( - static_ = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(static_ = it, _json = json) } + ?: ChunkingStrategy(_json = json) } } @@ -2714,6 +2860,25 @@ private constructor( 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 = + (static_.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("static")) 1 else 0 } + class Static private constructor( private val chunkOverlapTokens: JsonField, @@ -2918,6 +3083,25 @@ private constructor( 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 (chunkOverlapTokens.asKnown().isPresent) 1 else 0) + + (if (maxChunkSizeTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3033,6 +3217,26 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleted.kt index 6f2905dc9..807cc7887 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleted.kt @@ -201,6 +201,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("assistant.deleted")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt index c9e39876b..696f3b0f6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.beta.AssistantService import java.util.Collections import java.util.Objects @@ -117,6 +118,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt index e512c3b35..c784c1c29 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.beta.AssistantServiceAsync import java.util.Collections import java.util.Objects @@ -119,6 +120,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListParams.kt index 18612f48d..f7a3dde2a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListParams.kt @@ -347,6 +347,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantStreamEvent.kt index 5870d4c49..915f0eea8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantStreamEvent.kt @@ -447,8 +447,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { threadCreated != null -> visitor.visitThreadCreated(threadCreated) threadRunCreated != null -> visitor.visitThreadRunCreated(threadRunCreated) threadRunQueued != null -> visitor.visitThreadRunQueued(threadRunQueued) @@ -482,7 +482,6 @@ private constructor( errorEvent != null -> visitor.visitErrorEvent(errorEvent) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -607,6 +606,105 @@ private constructor( 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 visitThreadCreated(threadCreated: ThreadCreated) = + threadCreated.validity() + + override fun visitThreadRunCreated(threadRunCreated: ThreadRunCreated) = + threadRunCreated.validity() + + override fun visitThreadRunQueued(threadRunQueued: ThreadRunQueued) = + threadRunQueued.validity() + + override fun visitThreadRunInProgress(threadRunInProgress: ThreadRunInProgress) = + threadRunInProgress.validity() + + override fun visitThreadRunRequiresAction( + threadRunRequiresAction: ThreadRunRequiresAction + ) = threadRunRequiresAction.validity() + + override fun visitThreadRunCompleted(threadRunCompleted: ThreadRunCompleted) = + threadRunCompleted.validity() + + override fun visitThreadRunIncomplete(threadRunIncomplete: ThreadRunIncomplete) = + threadRunIncomplete.validity() + + override fun visitThreadRunFailed(threadRunFailed: ThreadRunFailed) = + threadRunFailed.validity() + + override fun visitThreadRunCancelling(threadRunCancelling: ThreadRunCancelling) = + threadRunCancelling.validity() + + override fun visitThreadRunCancelled(threadRunCancelled: ThreadRunCancelled) = + threadRunCancelled.validity() + + override fun visitThreadRunExpired(threadRunExpired: ThreadRunExpired) = + threadRunExpired.validity() + + override fun visitThreadRunStepCreated(threadRunStepCreated: ThreadRunStepCreated) = + threadRunStepCreated.validity() + + override fun visitThreadRunStepInProgress( + threadRunStepInProgress: ThreadRunStepInProgress + ) = threadRunStepInProgress.validity() + + override fun visitThreadRunStepDelta(threadRunStepDelta: ThreadRunStepDelta) = + threadRunStepDelta.validity() + + override fun visitThreadRunStepCompleted( + threadRunStepCompleted: ThreadRunStepCompleted + ) = threadRunStepCompleted.validity() + + override fun visitThreadRunStepFailed(threadRunStepFailed: ThreadRunStepFailed) = + threadRunStepFailed.validity() + + override fun visitThreadRunStepCancelled( + threadRunStepCancelled: ThreadRunStepCancelled + ) = threadRunStepCancelled.validity() + + override fun visitThreadRunStepExpired(threadRunStepExpired: ThreadRunStepExpired) = + threadRunStepExpired.validity() + + override fun visitThreadMessageCreated(threadMessageCreated: ThreadMessageCreated) = + threadMessageCreated.validity() + + override fun visitThreadMessageInProgress( + threadMessageInProgress: ThreadMessageInProgress + ) = threadMessageInProgress.validity() + + override fun visitThreadMessageDelta(threadMessageDelta: ThreadMessageDelta) = + threadMessageDelta.validity() + + override fun visitThreadMessageCompleted( + threadMessageCompleted: ThreadMessageCompleted + ) = threadMessageCompleted.validity() + + override fun visitThreadMessageIncomplete( + threadMessageIncomplete: ThreadMessageIncomplete + ) = threadMessageIncomplete.validity() + + override fun visitErrorEvent(errorEvent: ErrorEvent) = errorEvent.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1041,166 +1139,124 @@ private constructor( when (event) { "thread.created" -> { - return AssistantStreamEvent( - threadCreated = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadCreated = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.created" -> { - return AssistantStreamEvent( - threadRunCreated = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunCreated = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.queued" -> { - return AssistantStreamEvent( - threadRunQueued = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunQueued = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.in_progress" -> { - return AssistantStreamEvent( - threadRunInProgress = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunInProgress = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.requires_action" -> { - return AssistantStreamEvent( - threadRunRequiresAction = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunRequiresAction = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.completed" -> { - return AssistantStreamEvent( - threadRunCompleted = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunCompleted = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.incomplete" -> { - return AssistantStreamEvent( - threadRunIncomplete = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunIncomplete = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.failed" -> { - return AssistantStreamEvent( - threadRunFailed = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunFailed = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.cancelling" -> { - return AssistantStreamEvent( - threadRunCancelling = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunCancelling = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.cancelled" -> { - return AssistantStreamEvent( - threadRunCancelled = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunCancelled = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.expired" -> { - return AssistantStreamEvent( - threadRunExpired = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunExpired = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.created" -> { - return AssistantStreamEvent( - threadRunStepCreated = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepCreated = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.in_progress" -> { - return AssistantStreamEvent( - threadRunStepInProgress = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepInProgress = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.delta" -> { - return AssistantStreamEvent( - threadRunStepDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepDelta = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.completed" -> { - return AssistantStreamEvent( - threadRunStepCompleted = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepCompleted = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.failed" -> { - return AssistantStreamEvent( - threadRunStepFailed = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepFailed = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.cancelled" -> { - return AssistantStreamEvent( - threadRunStepCancelled = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepCancelled = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.run.step.expired" -> { - return AssistantStreamEvent( - threadRunStepExpired = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadRunStepExpired = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.message.created" -> { - return AssistantStreamEvent( - threadMessageCreated = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadMessageCreated = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.message.in_progress" -> { - return AssistantStreamEvent( - threadMessageInProgress = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadMessageInProgress = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.message.delta" -> { - return AssistantStreamEvent( - threadMessageDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadMessageDelta = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.message.completed" -> { - return AssistantStreamEvent( - threadMessageCompleted = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadMessageCompleted = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "thread.message.incomplete" -> { - return AssistantStreamEvent( - threadMessageIncomplete = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(threadMessageIncomplete = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } "error" -> { - return AssistantStreamEvent( - errorEvent = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantStreamEvent(errorEvent = it, _json = json) + } ?: AssistantStreamEvent(_json = json) } } @@ -1460,6 +1516,26 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.created")) 1 else 0 } + + (if (enabled.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1645,6 +1721,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1830,6 +1925,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.queued")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2015,6 +2129,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2200,6 +2333,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.requires_action")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2384,6 +2536,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2569,6 +2740,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.incomplete")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2751,6 +2941,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.failed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2936,6 +3145,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.cancelling")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3120,6 +3348,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.cancelled")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3302,6 +3549,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.expired")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3483,6 +3749,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3665,6 +3950,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3851,6 +4155,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4033,6 +4356,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4214,6 +4556,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.failed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4396,6 +4757,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.cancelled")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4577,6 +4957,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.expired")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4762,6 +5161,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4947,6 +5365,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5130,6 +5567,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5315,6 +5771,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5500,6 +5975,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.incomplete")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5674,6 +6168,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("error")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantTool.kt index 0acd60b84..1d2261a75 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantTool.kt @@ -48,14 +48,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -82,6 +81,34 @@ private constructor( 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 visitCodeInterpreter(codeInterpreter: CodeInterpreterTool) = + codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: FileSearchTool) = fileSearch.validity() + + override fun visitFunction(function: FunctionTool) = function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -146,22 +173,19 @@ private constructor( when (type) { "code_interpreter" -> { - return AssistantTool( - codeInterpreter = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantTool(codeInterpreter = it, _json = json) + } ?: AssistantTool(_json = json) } "file_search" -> { - return AssistantTool( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantTool(fileSearch = it, _json = json) + } ?: AssistantTool(_json = json) } "function" -> { - return AssistantTool( - function = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AssistantTool(function = it, _json = json) + } ?: AssistantTool(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt index 2d3bfcbec..556269003 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt @@ -1461,7 +1461,7 @@ private constructor( metadata().ifPresent { it.validate() } model() name() - reasoningEffort() + reasoningEffort().ifPresent { it.validate() } responseFormat().ifPresent { it.validate() } temperature() toolResources().ifPresent { it.validate() } @@ -1470,6 +1470,34 @@ private constructor( 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 (description.asKnown().isPresent) 1 else 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1562,6 +1590,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1838,6 +1884,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Model = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2005,6 +2078,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -2144,6 +2236,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2304,6 +2412,23 @@ private constructor( 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 = (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/CodeInterpreterTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/CodeInterpreterTool.kt index a0f04b7a2..e79c0d336 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/CodeInterpreterTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/CodeInterpreterTool.kt @@ -124,6 +124,23 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("code_interpreter")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FileSearchTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FileSearchTool.kt index 6a3fabcbd..0aca3ceca 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FileSearchTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FileSearchTool.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class FileSearchTool private constructor( @@ -165,6 +166,24 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("file_search")) 1 else 0 } + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + /** Overrides for the file search tool. */ class FileSearch private constructor( @@ -349,6 +368,25 @@ private constructor( 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 (maxNumResults.asKnown().isPresent) 1 else 0) + + (rankingOptions.asKnown().getOrNull()?.validity() ?: 0) + /** * The ranking options for the file search. If not specified, the file search tool will use * the `auto` ranker and a score_threshold of 0. @@ -529,10 +567,29 @@ private constructor( } scoreThreshold() - ranker() + ranker().ifPresent { it.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 = + (if (scoreThreshold.asKnown().isPresent) 1 else 0) + + (ranker.asKnown().getOrNull()?.validity() ?: 0) + /** * The ranker to use for the file search. If not specified will use the `auto` ranker. */ @@ -627,6 +684,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Ranker = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FunctionTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FunctionTool.kt index c5f987185..91f91bad3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FunctionTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/FunctionTool.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import com.openai.models.FunctionDefinition import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class FunctionTool private constructor( @@ -178,6 +179,24 @@ private constructor( 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 = + (function.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/MessageStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/MessageStreamEvent.kt index 50d3e5127..aba8270e6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/MessageStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/MessageStreamEvent.kt @@ -126,8 +126,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { threadMessageCreated != null -> visitor.visitThreadMessageCreated(threadMessageCreated) threadMessageInProgress != null -> visitor.visitThreadMessageInProgress(threadMessageInProgress) @@ -138,7 +138,6 @@ private constructor( visitor.visitThreadMessageIncomplete(threadMessageIncomplete) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -179,6 +178,45 @@ private constructor( 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 visitThreadMessageCreated(threadMessageCreated: ThreadMessageCreated) = + threadMessageCreated.validity() + + override fun visitThreadMessageInProgress( + threadMessageInProgress: ThreadMessageInProgress + ) = threadMessageInProgress.validity() + + override fun visitThreadMessageDelta(threadMessageDelta: ThreadMessageDelta) = + threadMessageDelta.validity() + + override fun visitThreadMessageCompleted( + threadMessageCompleted: ThreadMessageCompleted + ) = threadMessageCompleted.validity() + + override fun visitThreadMessageIncomplete( + threadMessageIncomplete: ThreadMessageIncomplete + ) = threadMessageIncomplete.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -309,39 +347,29 @@ private constructor( when (event) { "thread.message.created" -> { - return MessageStreamEvent( - threadMessageCreated = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageStreamEvent(threadMessageCreated = it, _json = json) + } ?: MessageStreamEvent(_json = json) } "thread.message.in_progress" -> { - return MessageStreamEvent( - threadMessageInProgress = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageStreamEvent(threadMessageInProgress = it, _json = json) + } ?: MessageStreamEvent(_json = json) } "thread.message.delta" -> { - return MessageStreamEvent( - threadMessageDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageStreamEvent(threadMessageDelta = it, _json = json) + } ?: MessageStreamEvent(_json = json) } "thread.message.completed" -> { - return MessageStreamEvent( - threadMessageCompleted = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageStreamEvent(threadMessageCompleted = it, _json = json) + } ?: MessageStreamEvent(_json = json) } "thread.message.incomplete" -> { - return MessageStreamEvent( - threadMessageIncomplete = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageStreamEvent(threadMessageIncomplete = it, _json = json) + } ?: MessageStreamEvent(_json = json) } } @@ -539,6 +567,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -724,6 +771,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -907,6 +973,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1092,6 +1177,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1277,6 +1381,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.message.incomplete")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStepStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStepStreamEvent.kt index c1d5a3fe8..18befa6a7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStepStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStepStreamEvent.kt @@ -168,8 +168,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { threadRunStepCreated != null -> visitor.visitThreadRunStepCreated(threadRunStepCreated) threadRunStepInProgress != null -> visitor.visitThreadRunStepInProgress(threadRunStepInProgress) @@ -182,7 +182,6 @@ private constructor( threadRunStepExpired != null -> visitor.visitThreadRunStepExpired(threadRunStepExpired) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -231,6 +230,51 @@ private constructor( 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 visitThreadRunStepCreated(threadRunStepCreated: ThreadRunStepCreated) = + threadRunStepCreated.validity() + + override fun visitThreadRunStepInProgress( + threadRunStepInProgress: ThreadRunStepInProgress + ) = threadRunStepInProgress.validity() + + override fun visitThreadRunStepDelta(threadRunStepDelta: ThreadRunStepDelta) = + threadRunStepDelta.validity() + + override fun visitThreadRunStepCompleted( + threadRunStepCompleted: ThreadRunStepCompleted + ) = threadRunStepCompleted.validity() + + override fun visitThreadRunStepFailed(threadRunStepFailed: ThreadRunStepFailed) = + threadRunStepFailed.validity() + + override fun visitThreadRunStepCancelled( + threadRunStepCancelled: ThreadRunStepCancelled + ) = threadRunStepCancelled.validity() + + override fun visitThreadRunStepExpired(threadRunStepExpired: ThreadRunStepExpired) = + threadRunStepExpired.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -401,53 +445,39 @@ private constructor( when (event) { "thread.run.step.created" -> { - return RunStepStreamEvent( - threadRunStepCreated = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepCreated = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } "thread.run.step.in_progress" -> { - return RunStepStreamEvent( - threadRunStepInProgress = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepInProgress = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } "thread.run.step.delta" -> { - return RunStepStreamEvent( - threadRunStepDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepDelta = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } "thread.run.step.completed" -> { - return RunStepStreamEvent( - threadRunStepCompleted = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepCompleted = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } "thread.run.step.failed" -> { - return RunStepStreamEvent( - threadRunStepFailed = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepFailed = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } "thread.run.step.cancelled" -> { - return RunStepStreamEvent( - threadRunStepCancelled = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepCancelled = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } "thread.run.step.expired" -> { - return RunStepStreamEvent( - threadRunStepExpired = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStepStreamEvent(threadRunStepExpired = it, _json = json) + } ?: RunStepStreamEvent(_json = json) } } @@ -645,6 +675,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -827,6 +876,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1013,6 +1081,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1195,6 +1282,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1376,6 +1482,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.failed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1558,6 +1683,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.cancelled")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1739,6 +1883,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.step.expired")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStreamEvent.kt index 67ae03da6..51f862b81 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/RunStreamEvent.kt @@ -184,8 +184,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { threadRunCreated != null -> visitor.visitThreadRunCreated(threadRunCreated) threadRunQueued != null -> visitor.visitThreadRunQueued(threadRunQueued) threadRunInProgress != null -> visitor.visitThreadRunInProgress(threadRunInProgress) @@ -199,7 +199,6 @@ private constructor( threadRunExpired != null -> visitor.visitThreadRunExpired(threadRunExpired) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -256,6 +255,58 @@ private constructor( 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 visitThreadRunCreated(threadRunCreated: ThreadRunCreated) = + threadRunCreated.validity() + + override fun visitThreadRunQueued(threadRunQueued: ThreadRunQueued) = + threadRunQueued.validity() + + override fun visitThreadRunInProgress(threadRunInProgress: ThreadRunInProgress) = + threadRunInProgress.validity() + + override fun visitThreadRunRequiresAction( + threadRunRequiresAction: ThreadRunRequiresAction + ) = threadRunRequiresAction.validity() + + override fun visitThreadRunCompleted(threadRunCompleted: ThreadRunCompleted) = + threadRunCompleted.validity() + + override fun visitThreadRunIncomplete(threadRunIncomplete: ThreadRunIncomplete) = + threadRunIncomplete.validity() + + override fun visitThreadRunFailed(threadRunFailed: ThreadRunFailed) = + threadRunFailed.validity() + + override fun visitThreadRunCancelling(threadRunCancelling: ThreadRunCancelling) = + threadRunCancelling.validity() + + override fun visitThreadRunCancelled(threadRunCancelled: ThreadRunCancelled) = + threadRunCancelled.validity() + + override fun visitThreadRunExpired(threadRunExpired: ThreadRunExpired) = + threadRunExpired.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -453,70 +504,54 @@ private constructor( when (event) { "thread.run.created" -> { - return RunStreamEvent( - threadRunCreated = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunCreated = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.queued" -> { - return RunStreamEvent( - threadRunQueued = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunQueued = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.in_progress" -> { - return RunStreamEvent( - threadRunInProgress = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunInProgress = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.requires_action" -> { - return RunStreamEvent( - threadRunRequiresAction = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunRequiresAction = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.completed" -> { - return RunStreamEvent( - threadRunCompleted = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunCompleted = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.incomplete" -> { - return RunStreamEvent( - threadRunIncomplete = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunIncomplete = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.failed" -> { - return RunStreamEvent( - threadRunFailed = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunFailed = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.cancelling" -> { - return RunStreamEvent( - threadRunCancelling = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunCancelling = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.cancelled" -> { - return RunStreamEvent( - threadRunCancelled = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunCancelled = it, _json = json) + } ?: RunStreamEvent(_json = json) } "thread.run.expired" -> { - return RunStreamEvent( - threadRunExpired = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + RunStreamEvent(threadRunExpired = it, _json = json) + } ?: RunStreamEvent(_json = json) } } @@ -719,6 +754,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -904,6 +958,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.queued")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1089,6 +1162,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1274,6 +1366,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.requires_action")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1458,6 +1569,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1643,6 +1773,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.incomplete")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1825,6 +1974,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.failed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2010,6 +2178,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.cancelling")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2194,6 +2381,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.cancelled")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2376,6 +2582,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.run.expired")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/ThreadStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/ThreadStreamEvent.kt index ff540ff2b..6331fb7f4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/ThreadStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/ThreadStreamEvent.kt @@ -16,6 +16,7 @@ import com.openai.models.beta.threads.Thread import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Occurs when a new [thread](https://platform.openai.com/docs/api-reference/threads/object) is @@ -215,6 +216,25 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + event.let { if (it == JsonValue.from("thread.created")) 1 else 0 } + + (if (enabled.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOption.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOption.kt index 31b3815db..f6db5fda5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOption.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOption.kt @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.BaseDeserializer import com.openai.core.BaseSerializer import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import com.openai.models.ResponseFormatJsonObject @@ -104,8 +105,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) responseFormatText != null -> visitor.visitResponseFormatText(responseFormatText) responseFormatJsonObject != null -> @@ -114,7 +115,6 @@ private constructor( visitor.visitResponseFormatJsonSchema(responseFormatJsonSchema) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -153,6 +153,41 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitResponseFormatText(responseFormatText: ResponseFormatText) = + responseFormatText.validity() + + override fun visitResponseFormatJsonObject( + responseFormatJsonObject: ResponseFormatJsonObject + ) = responseFormatJsonObject.validity() + + override fun visitResponseFormatJsonSchema( + responseFormatJsonSchema: ResponseFormatJsonSchema + ) = responseFormatJsonSchema.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -250,36 +285,39 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): AssistantResponseFormatOption { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException("'auto' is invalid, received $it") - } - } - } - ?.let { - return AssistantResponseFormatOption(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return AssistantResponseFormatOption(responseFormatText = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return AssistantResponseFormatOption( - responseFormatJsonObject = it, - _json = json, - ) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return AssistantResponseFormatOption( - responseFormatJsonSchema = it, - _json = json, + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { AssistantResponseFormatOption(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + AssistantResponseFormatOption(responseFormatText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AssistantResponseFormatOption( + responseFormatJsonObject = it, + _json = json, + ) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AssistantResponseFormatOption( + responseFormatJsonSchema = it, + _json = json, + ) + }, ) - } - - return AssistantResponseFormatOption(_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 -> AssistantResponseFormatOption(_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() + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoice.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoice.kt index a42e1d4d5..59335547a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoice.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoice.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Specifies a tool the model should use. Use to force the model to call a specific tool. */ class AssistantToolChoice @@ -173,11 +174,29 @@ private constructor( return@apply } - type() + type().validate() function().ifPresent { it.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 = + (type.asKnown().getOrNull()?.validity() ?: 0) + + (function.asKnown().getOrNull()?.validity() ?: 0) + /** The type of the tool. If type is `function`, the function name must be set */ class Type @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -270,6 +289,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunction.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunction.kt index 30ad422f3..d6d82f1ed 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunction.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunction.kt @@ -138,6 +138,21 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOption.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOption.kt index 51e1f2931..b96b77f71 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOption.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOption.kt @@ -15,6 +15,7 @@ import com.openai.core.BaseSerializer import com.openai.core.Enum import com.openai.core.JsonField import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -65,13 +66,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) assistantToolChoice != null -> visitor.visitAssistantToolChoice(assistantToolChoice) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -82,7 +82,9 @@ private constructor( accept( object : Visitor { - override fun visitAuto(auto: Auto) {} + override fun visitAuto(auto: Auto) { + auto.validate() + } override fun visitAssistantToolChoice(assistantToolChoice: AssistantToolChoice) { assistantToolChoice.validate() @@ -92,6 +94,32 @@ private constructor( 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 visitAuto(auto: Auto) = auto.validity() + + override fun visitAssistantToolChoice(assistantToolChoice: AssistantToolChoice) = + assistantToolChoice.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -167,15 +195,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): AssistantToolChoiceOption { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return AssistantToolChoiceOption(auto = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + AssistantToolChoiceOption(auto = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AssistantToolChoiceOption(assistantToolChoice = 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 -> AssistantToolChoiceOption(_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() } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return AssistantToolChoiceOption(assistantToolChoice = it, _json = json) - } - - return AssistantToolChoiceOption(_json = json) } } @@ -293,6 +333,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Auto = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/Thread.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/Thread.kt index 030ed52d7..28890552f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/Thread.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/Thread.kt @@ -321,6 +321,27 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("thread")) 1 else 0 } + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing * additional information about the object in a structured format, and querying for objects via @@ -395,6 +416,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -566,6 +605,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -704,6 +762,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -864,6 +938,23 @@ private constructor( 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 = (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateAndRunParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateAndRunParams.kt index 84ddab3ba..dcfcd309b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateAndRunParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateAndRunParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -1893,7 +1894,7 @@ private constructor( maxCompletionTokens() maxPromptTokens() metadata().ifPresent { it.validate() } - model() + model().ifPresent { it.validate() } parallelToolCalls() responseFormat().ifPresent { it.validate() } temperature() @@ -1906,6 +1907,38 @@ private constructor( 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 (assistantId.asKnown().isPresent) 1 else 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (if (maxCompletionTokens.asKnown().isPresent) 1 else 0) + + (if (maxPromptTokens.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + (if (parallelToolCalls.asKnown().isPresent) 1 else 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (thread.asKnown().getOrNull()?.validity() ?: 0) + + (toolChoice.asKnown().getOrNull()?.validity() ?: 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + + (truncationStrategy.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1998,6 +2031,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2256,6 +2307,26 @@ private constructor( 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 = + (messages.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + class Message private constructor( private val content: JsonField, @@ -2551,12 +2622,33 @@ private constructor( } content().validate() - role() + role().validate() attachments().ifPresent { it.forEach { it.validate() } } metadata().ifPresent { it.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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (attachments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + /** The text contents of the message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -2595,14 +2687,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2625,6 +2716,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2699,17 +2818,31 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 + // object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } @@ -2828,6 +2961,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3016,6 +3176,25 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + @JsonDeserialize(using = Tool.Deserializer::class) @JsonSerialize(using = Tool.Serializer::class) class Tool @@ -3041,13 +3220,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3078,6 +3256,38 @@ private constructor( 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 visitCodeInterpreter( + codeInterpreter: CodeInterpreterTool + ) = codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: JsonValue) = + fileSearch.let { + if (it == JsonValue.from(mapOf("type" to "file_search"))) 1 + else 0 + } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3141,20 +3351,17 @@ private constructor( when (type) { "code_interpreter" -> { - return Tool( - codeInterpreter = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { Tool(codeInterpreter = it, _json = json) } + ?: Tool(_json = json) } "file_search" -> { - return Tool( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(fileSearch = it, _json = json) } + ?.takeIf { it.isValid() } ?: Tool(_json = json) } } @@ -3275,6 +3482,26 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3387,6 +3614,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3563,6 +3808,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -3704,6 +3968,23 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3926,6 +4207,25 @@ private constructor( 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 = + (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + + (vectorStores.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class VectorStore private constructor( private val chunkingStrategy: JsonField, @@ -4197,6 +4497,26 @@ private constructor( 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 = + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (fileIds.asKnown().getOrNull()?.size ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + /** * The chunking strategy used to chunk the file(s). If not set, will use the * `auto` strategy. @@ -4232,13 +4552,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) static_ != null -> visitor.visitStatic(static_) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4267,6 +4586,37 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { + if (it == JsonValue.from(mapOf("type" to "auto"))) 1 + else 0 + } + + override fun visitStatic(static_: StaticObject) = + static_.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4344,17 +4694,15 @@ private constructor( when (type) { "auto" -> { - return ChunkingStrategy( - auto = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(auto = it, _json = json) } + ?.takeIf { it.isValid() } + ?: ChunkingStrategy(_json = json) } "static" -> { - return ChunkingStrategy( - static_ = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(static_ = it, _json = json) } + ?: ChunkingStrategy(_json = json) } } @@ -4557,6 +4905,25 @@ private constructor( 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 = + (static_.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("static")) 1 else 0 } + class Static private constructor( private val chunkOverlapTokens: JsonField, @@ -4768,6 +5135,25 @@ private constructor( 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 (chunkOverlapTokens.asKnown().isPresent) 1 else 0) + + (if (maxChunkSizeTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4886,6 +5272,26 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5130,6 +5536,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -5268,6 +5693,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5428,6 +5869,23 @@ private constructor( 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 = (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5494,14 +5952,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -5528,6 +5985,35 @@ private constructor( 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 visitCodeInterpreter(codeInterpreter: CodeInterpreterTool) = + codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: FileSearchTool) = fileSearch.validity() + + override fun visitFunction(function: FunctionTool) = function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5586,20 +6072,31 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Tool { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Tool(codeInterpreter = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Tool(fileSearch = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Tool(function = it, _json = json) - } - - return Tool(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Tool(codeInterpreter = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Tool(fileSearch = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Tool(function = 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 boolean). + 0 -> Tool(_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() + } } } @@ -5807,11 +6304,30 @@ private constructor( return@apply } - type() + type().validate() lastMessages() 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 = + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (lastMessages.asKnown().isPresent) 1 else 0) + /** * The truncation strategy to use for the thread. The default is `auto`. If set to * `last_messages`, the thread will be truncated to the n most recent messages in the @@ -5905,6 +6421,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateParams.kt index 0028488e3..7c1c81680 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -572,6 +573,26 @@ private constructor( 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 = + (messages.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -870,12 +891,33 @@ private constructor( } content().validate() - role() + role().validate() attachments().ifPresent { it.forEach { it.validate() } } metadata().ifPresent { it.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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (attachments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + /** The text contents of the message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -914,14 +956,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -944,6 +985,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1014,17 +1083,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } @@ -1140,6 +1222,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1324,6 +1433,25 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + @JsonDeserialize(using = Tool.Deserializer::class) @JsonSerialize(using = Tool.Serializer::class) class Tool @@ -1349,13 +1477,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1386,6 +1513,38 @@ private constructor( 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 visitCodeInterpreter( + codeInterpreter: CodeInterpreterTool + ) = codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: JsonValue) = + fileSearch.let { + if (it == JsonValue.from(mapOf("type" to "file_search"))) 1 + else 0 + } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1448,17 +1607,14 @@ private constructor( when (type) { "code_interpreter" -> { - return Tool( - codeInterpreter = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(codeInterpreter = it, _json = json) } + ?: Tool(_json = json) } "file_search" -> { - return Tool( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(fileSearch = it, _json = json) } + ?.takeIf { it.isValid() } ?: Tool(_json = json) } } @@ -1579,6 +1735,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1688,6 +1862,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1859,6 +2051,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -1997,6 +2208,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2219,6 +2446,25 @@ private constructor( 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 = + (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + + (vectorStores.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class VectorStore private constructor( private val chunkingStrategy: JsonField, @@ -2480,6 +2726,26 @@ private constructor( 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 = + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (fileIds.asKnown().getOrNull()?.size ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + /** * The chunking strategy used to chunk the file(s). If not set, will use the `auto` * strategy. @@ -2515,13 +2781,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) static_ != null -> visitor.visitStatic(static_) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2550,6 +2815,35 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { + if (it == JsonValue.from(mapOf("type" to "auto"))) 1 else 0 + } + + override fun visitStatic(static_: StaticObject) = static_.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2621,16 +2915,14 @@ private constructor( when (type) { "auto" -> { - return ChunkingStrategy( - auto = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(auto = it, _json = json) } + ?.takeIf { it.isValid() } ?: ChunkingStrategy(_json = json) } "static" -> { - return ChunkingStrategy( - static_ = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(static_ = it, _json = json) } + ?: ChunkingStrategy(_json = json) } } @@ -2830,6 +3122,25 @@ private constructor( 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 = + (static_.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("static")) 1 else 0 } + class Static private constructor( private val chunkOverlapTokens: JsonField, @@ -3034,6 +3345,25 @@ private constructor( 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 (chunkOverlapTokens.asKnown().isPresent) 1 else 0) + + (if (maxChunkSizeTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3149,6 +3479,26 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleted.kt index 7b0639b41..ef5977682 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleted.kt @@ -201,6 +201,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("thread.deleted")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt index 8503344b9..e7283d40b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt @@ -482,6 +482,25 @@ private constructor( 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 = + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (toolResources.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -574,6 +593,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -745,6 +782,25 @@ private constructor( 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 = + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + class CodeInterpreter private constructor( private val fileIds: JsonField>, @@ -883,6 +939,22 @@ private constructor( 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 = (fileIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1043,6 +1115,23 @@ private constructor( 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 = (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Annotation.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Annotation.kt index c10615b7b..8acf3bd94 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Annotation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Annotation.kt @@ -64,13 +64,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { fileCitation != null -> visitor.visitFileCitation(fileCitation) filePath != null -> visitor.visitFilePath(filePath) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -93,6 +92,32 @@ private constructor( 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 visitFileCitation(fileCitation: FileCitationAnnotation) = + fileCitation.validity() + + override fun visitFilePath(filePath: FilePathAnnotation) = filePath.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -167,16 +192,14 @@ private constructor( when (type) { "file_citation" -> { - return Annotation( - fileCitation = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(fileCitation = it, _json = json) + } ?: Annotation(_json = json) } "file_path" -> { - return Annotation( - filePath = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(filePath = it, _json = json) + } ?: Annotation(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/AnnotationDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/AnnotationDelta.kt index 79cb799e9..4d68081c9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/AnnotationDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/AnnotationDelta.kt @@ -64,13 +64,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { fileCitation != null -> visitor.visitFileCitation(fileCitation) filePath != null -> visitor.visitFilePath(filePath) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -93,6 +92,32 @@ private constructor( 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 visitFileCitation(fileCitation: FileCitationDeltaAnnotation) = + fileCitation.validity() + + override fun visitFilePath(filePath: FilePathDeltaAnnotation) = filePath.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -172,17 +197,14 @@ private constructor( when (type) { "file_citation" -> { - return AnnotationDelta( - fileCitation = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { AnnotationDelta(fileCitation = it, _json = json) } + ?: AnnotationDelta(_json = json) } "file_path" -> { - return AnnotationDelta( - filePath = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + AnnotationDelta(filePath = it, _json = json) + } ?: AnnotationDelta(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotation.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotation.kt index 7c1587db5..3fc405435 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotation.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * A citation within the message that points to a specific quote from a specific File associated @@ -280,6 +281,27 @@ private constructor( 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 (endIndex.asKnown().isPresent) 1 else 0) + + (fileCitation.asKnown().getOrNull()?.validity() ?: 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_citation")) 1 else 0 } + class FileCitation private constructor( private val fileId: JsonField, @@ -401,6 +423,22 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotation.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotation.kt index 8b3fe53f6..4b8e0d279 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotation.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A citation within the message that points to a specific quote from a specific File associated @@ -308,6 +309,28 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_citation")) 1 else 0 } + + (if (endIndex.asKnown().isPresent) 1 else 0) + + (fileCitation.asKnown().getOrNull()?.validity() ?: 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + class FileCitation private constructor( private val fileId: JsonField, @@ -447,6 +470,24 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + (if (quote.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotation.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotation.kt index 60dde1029..14541aaf9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotation.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * A URL for the file that's generated when the assistant used the `code_interpreter` tool to @@ -273,6 +274,27 @@ private constructor( 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 (endIndex.asKnown().isPresent) 1 else 0) + + (filePath.asKnown().getOrNull()?.validity() ?: 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_path")) 1 else 0 } + class FilePath private constructor( private val fileId: JsonField, @@ -394,6 +416,22 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotation.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotation.kt index 613744ae9..213b056b8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotation.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A URL for the file that's generated when the assistant used the `code_interpreter` tool to @@ -300,6 +301,28 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_path")) 1 else 0 } + + (if (endIndex.asKnown().isPresent) 1 else 0) + + (filePath.asKnown().getOrNull()?.validity() ?: 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + class FilePath private constructor( private val fileId: JsonField, @@ -406,6 +429,22 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFile.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFile.kt index d215fc030..0850ecc04 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFile.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFile.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ImageFile private constructor( @@ -174,10 +175,27 @@ private constructor( } fileId() - detail() + detail().ifPresent { it.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 = + (if (fileId.asKnown().isPresent) 1 else 0) + (detail.asKnown().getOrNull()?.validity() ?: 0) + /** * Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, * you can opt in to high resolution using `high`. @@ -273,6 +291,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Detail = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlock.kt index d05bdf36d..7401591b9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlock.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * References an image [File](https://platform.openai.com/docs/api-reference/files) in the content @@ -179,6 +180,24 @@ private constructor( 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 = + (imageFile.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("image_file")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDelta.kt index 6db1a1b06..719ca5470 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDelta.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ImageFileDelta private constructor( @@ -158,11 +159,28 @@ private constructor( return@apply } - detail() + detail().ifPresent { it.validate() } fileId() 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 = + (detail.asKnown().getOrNull()?.validity() ?: 0) + (if (fileId.asKnown().isPresent) 1 else 0) + /** * Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, * you can opt in to high resolution using `high`. @@ -258,6 +276,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Detail = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlock.kt index df5ee6635..b6e2656b8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlock.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * References an image [File](https://platform.openai.com/docs/api-reference/files) in the content @@ -215,6 +216,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("image_file")) 1 else 0 } + + (imageFile.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrl.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrl.kt index 52b421758..bdd63cd2c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrl.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ImageUrl private constructor( @@ -171,10 +172,27 @@ private constructor( } url() - detail() + detail().ifPresent { it.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 = + (if (url.asKnown().isPresent) 1 else 0) + (detail.asKnown().getOrNull()?.validity() ?: 0) + /** * Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high * resolution using `high`. Default value is `auto` @@ -270,6 +288,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Detail = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlock.kt index 17526feea..f56491e04 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlock.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** References an image URL in the content of a message. */ class ImageUrlContentBlock @@ -174,6 +175,24 @@ private constructor( 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 = + (imageUrl.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("image_url")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDelta.kt index a0e9393b0..74da5023a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDelta.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ImageUrlDelta private constructor( @@ -151,11 +152,28 @@ private constructor( return@apply } - detail() + detail().ifPresent { it.validate() } url() 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 = + (detail.asKnown().getOrNull()?.validity() ?: 0) + (if (url.asKnown().isPresent) 1 else 0) + /** * Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high * resolution using `high`. @@ -251,6 +269,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Detail = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlock.kt index aa7f9c83a..579e8a4e8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlock.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** References an image URL in the content of a message. */ class ImageUrlDeltaBlock @@ -209,6 +210,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("image_url")) 1 else 0 } + + (imageUrl.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Message.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Message.kt index d0b1aacf9..63fb38b24 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Message.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Message.kt @@ -20,6 +20,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -802,13 +803,43 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - role() + role().validate() runId() - status() + status().validate() threadId() 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 (id.asKnown().isPresent) 1 else 0) + + (if (assistantId.asKnown().isPresent) 1 else 0) + + (attachments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (completedAt.asKnown().isPresent) 1 else 0) + + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (incompleteAt.asKnown().isPresent) 1 else 0) + + (incompleteDetails.asKnown().getOrNull()?.validity() ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("thread.message")) 1 else 0 } + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (if (runId.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (threadId.asKnown().isPresent) 1 else 0) + class Attachment private constructor( private val fileId: JsonField, @@ -973,6 +1004,25 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + @JsonDeserialize(using = Tool.Deserializer::class) @JsonSerialize(using = Tool.Serializer::class) class Tool @@ -998,13 +1048,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) object_ != null -> visitor.visitObject(object_) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1033,6 +1082,36 @@ private constructor( 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 visitCodeInterpreter(codeInterpreter: CodeInterpreterTool) = + codeInterpreter.validity() + + override fun visitObject(object_: JsonValue) = + object_.let { + if (it == JsonValue.from(mapOf("type" to "file_search"))) 1 else 0 + } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1090,24 +1169,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Tool { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Tool(codeInterpreter = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from(mapOf("type" to "file_search"))) { - throw OpenAIInvalidDataException( - "'object_' is invalid, received $it" - ) - } - } - } - ?.let { - return Tool(object_ = it, _json = json) - } - - return Tool(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(object_ = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + Tool(codeInterpreter = 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 boolean). + 0 -> Tool(_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() + } } } @@ -1268,10 +1351,26 @@ private constructor( return@apply } - reason() + reason().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 = (reason.asKnown().getOrNull()?.validity() ?: 0) + /** The reason the message is incomplete. */ class Reason @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1380,6 +1479,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Reason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1485,6 +1611,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1588,6 +1732,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1695,6 +1866,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContent.kt index 77c69e787..45845e1ee 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContent.kt @@ -73,15 +73,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { imageFile != null -> visitor.visitImageFile(imageFile) imageUrl != null -> visitor.visitImageUrl(imageUrl) text != null -> visitor.visitText(text) refusal != null -> visitor.visitRefusal(refusal) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -112,6 +111,35 @@ private constructor( 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 visitImageFile(imageFile: ImageFileContentBlock) = imageFile.validity() + + override fun visitImageUrl(imageUrl: ImageUrlContentBlock) = imageUrl.validity() + + override fun visitText(text: TextContentBlock) = text.validity() + + override fun visitRefusal(refusal: RefusalContentBlock) = refusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -195,28 +223,24 @@ private constructor( when (type) { "image_file" -> { - return MessageContent( - imageFile = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContent(imageFile = it, _json = json) + } ?: MessageContent(_json = json) } "image_url" -> { - return MessageContent( - imageUrl = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContent(imageUrl = it, _json = json) + } ?: MessageContent(_json = json) } "text" -> { - return MessageContent( - text = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContent(text = it, _json = json) + } ?: MessageContent(_json = json) } "refusal" -> { - return MessageContent( - refusal = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContent(refusal = it, _json = json) + } ?: MessageContent(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentDelta.kt index 6be6aef00..5705cc65f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentDelta.kt @@ -73,15 +73,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { imageFile != null -> visitor.visitImageFile(imageFile) text != null -> visitor.visitText(text) refusal != null -> visitor.visitRefusal(refusal) imageUrl != null -> visitor.visitImageUrl(imageUrl) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -112,6 +111,35 @@ private constructor( 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 visitImageFile(imageFile: ImageFileDeltaBlock) = imageFile.validity() + + override fun visitText(text: TextDeltaBlock) = text.validity() + + override fun visitRefusal(refusal: RefusalDeltaBlock) = refusal.validity() + + override fun visitImageUrl(imageUrl: ImageUrlDeltaBlock) = imageUrl.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -198,28 +226,24 @@ private constructor( when (type) { "image_file" -> { - return MessageContentDelta( - imageFile = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentDelta(imageFile = it, _json = json) + } ?: MessageContentDelta(_json = json) } "text" -> { - return MessageContentDelta( - text = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentDelta(text = it, _json = json) + } ?: MessageContentDelta(_json = json) } "refusal" -> { - return MessageContentDelta( - refusal = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentDelta(refusal = it, _json = json) + } ?: MessageContentDelta(_json = json) } "image_url" -> { - return MessageContentDelta( - imageUrl = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentDelta(imageUrl = it, _json = json) + } ?: MessageContentDelta(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParam.kt index 5a4b83681..321cc7a6d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParam.kt @@ -64,14 +64,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { imageFile != null -> visitor.visitImageFile(imageFile) imageUrl != null -> visitor.visitImageUrl(imageUrl) text != null -> visitor.visitText(text) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -98,6 +97,33 @@ private constructor( 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 visitImageFile(imageFile: ImageFileContentBlock) = imageFile.validity() + + override fun visitImageUrl(imageUrl: ImageUrlContentBlock) = imageUrl.validity() + + override fun visitText(text: TextContentBlockParam) = text.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -178,22 +204,19 @@ private constructor( when (type) { "image_file" -> { - return MessageContentPartParam( - imageFile = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentPartParam(imageFile = it, _json = json) + } ?: MessageContentPartParam(_json = json) } "image_url" -> { - return MessageContentPartParam( - imageUrl = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentPartParam(imageUrl = it, _json = json) + } ?: MessageContentPartParam(_json = json) } "text" -> { - return MessageContentPartParam( - text = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + MessageContentPartParam(text = it, _json = json) + } ?: MessageContentPartParam(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt index 21f927dfe..8834c0343 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -673,12 +674,33 @@ private constructor( } content().validate() - role() + role().validate() attachments().ifPresent { it.forEach { it.validate() } } metadata().ifPresent { it.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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (attachments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -735,13 +757,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -764,6 +785,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -832,17 +881,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } @@ -956,6 +1015,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1133,6 +1219,25 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + @JsonDeserialize(using = Tool.Deserializer::class) @JsonSerialize(using = Tool.Serializer::class) class Tool @@ -1158,13 +1263,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1193,6 +1297,36 @@ private constructor( 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 visitCodeInterpreter(codeInterpreter: CodeInterpreterTool) = + codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: JsonValue) = + fileSearch.let { + if (it == JsonValue.from(mapOf("type" to "file_search"))) 1 else 0 + } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1254,17 +1388,14 @@ private constructor( when (type) { "code_interpreter" -> { - return Tool( - codeInterpreter = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(codeInterpreter = it, _json = json) } + ?: Tool(_json = json) } "file_search" -> { - return Tool( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(fileSearch = it, _json = json) } + ?.takeIf { it.isValid() } ?: Tool(_json = json) } } @@ -1382,6 +1513,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleted.kt index 87c2300ee..57a94cbcd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleted.kt @@ -201,6 +201,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("thread.message.deleted")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDelta.kt index 1352ed676..742d94958 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDelta.kt @@ -17,6 +17,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** The delta containing the fields that have changed on the Message. */ class MessageDelta @@ -234,10 +235,28 @@ private constructor( } content().ifPresent { it.forEach { it.validate() } } - role() + role().ifPresent { it.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 = + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + /** The entity that produced the message. One of `user` or `assistant`. */ class Role @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -324,6 +343,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEvent.kt index 485308151..d9661c835 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Represents a message delta i.e. any changed fields on a message during streaming. */ class MessageDeltaEvent @@ -211,6 +212,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (delta.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("thread.message.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt index 028b56674..5be86146d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.beta.threads.MessageService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt index c2a37fec5..5549d4649 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.beta.threads.MessageServiceAsync import java.util.Collections import java.util.Objects @@ -119,6 +120,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt index 0ea09a371..0a1f8bbbc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt @@ -388,6 +388,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt index a8bdfb976..407578114 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt @@ -397,6 +397,23 @@ private constructor( 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 = (metadata.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -489,6 +506,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlock.kt index 07cbeab1b..6f47f0a98 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlock.kt @@ -173,6 +173,24 @@ private constructor( 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 (refusal.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("refusal")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlock.kt index fcdecedf2..8529d22e9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlock.kt @@ -206,6 +206,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("refusal")) 1 else 0 } + + (if (refusal.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Text.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Text.kt index ac811d529..226bc7ba6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Text.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/Text.kt @@ -16,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class Text private constructor( @@ -198,6 +199,24 @@ private constructor( 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 = + (annotations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlock.kt index 77f153a7f..962430752 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlock.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** The text content that is part of a message. */ class TextContentBlock @@ -169,6 +170,24 @@ private constructor( 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 = + (text.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParam.kt index 34c2721ff..d9b4c87d4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParam.kt @@ -176,6 +176,24 @@ private constructor( 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("text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDelta.kt index e5004b033..ce9885cf0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDelta.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class TextDelta private constructor( @@ -207,6 +208,24 @@ private constructor( 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 = + (annotations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlock.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlock.kt index 724a53c88..f91ac1e3b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlock.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlock.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** The text content that is part of a message. */ class TextDeltaBlock @@ -206,6 +207,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("text")) 1 else 0 } + + (text.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCall.kt index ef869509c..b39628b49 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCall.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Tool call objects */ class RequiredActionFunctionToolCall @@ -221,6 +222,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (function.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + /** The function definition. */ class Function private constructor( @@ -383,6 +403,24 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/Run.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/Run.kt index 54ad02d92..0852b9d57 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/Run.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/Run.kt @@ -1470,7 +1470,7 @@ private constructor( requiredAction().ifPresent { it.validate() } responseFormat().ifPresent { it.validate() } startedAt() - status() + status().validate() threadId() toolChoice().ifPresent { it.validate() } tools().forEach { it.validate() } @@ -1481,6 +1481,49 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (assistantId.asKnown().isPresent) 1 else 0) + + (if (cancelledAt.asKnown().isPresent) 1 else 0) + + (if (completedAt.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (failedAt.asKnown().isPresent) 1 else 0) + + (incompleteDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (lastError.asKnown().getOrNull()?.validity() ?: 0) + + (if (maxCompletionTokens.asKnown().isPresent) 1 else 0) + + (if (maxPromptTokens.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("thread.run")) 1 else 0 } + + (if (parallelToolCalls.asKnown().isPresent) 1 else 0) + + (requiredAction.asKnown().getOrNull()?.validity() ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (startedAt.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (threadId.asKnown().isPresent) 1 else 0) + + (toolChoice.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (truncationStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (if (topP.asKnown().isPresent) 1 else 0) + /** Details on why the run is incomplete. Will be `null` if the run is not incomplete. */ class IncompleteDetails private constructor( @@ -1589,10 +1632,26 @@ private constructor( return@apply } - reason() + reason().ifPresent { it.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 = (reason.asKnown().getOrNull()?.validity() ?: 0) + /** * The reason why the run is incomplete. This will point to which specific token limit was * reached over the course of the run. @@ -1686,6 +1745,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Reason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1872,11 +1958,30 @@ private constructor( return@apply } - code() + code().validate() message() 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 = + (code.asKnown().getOrNull()?.validity() ?: 0) + + (if (message.asKnown().isPresent) 1 else 0) + /** One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`. */ class Code @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1971,6 +2076,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Code = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2076,6 +2208,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2266,6 +2416,25 @@ private constructor( 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 = + (submitToolOutputs.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("submit_tool_outputs")) 1 else 0 } + /** Details on the tool outputs needed for this run to continue. */ class SubmitToolOutputs private constructor( @@ -2417,6 +2586,24 @@ private constructor( 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 = + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2639,11 +2826,30 @@ private constructor( return@apply } - type() + type().validate() lastMessages() 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 = + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (lastMessages.asKnown().isPresent) 1 else 0) + /** * The truncation strategy to use for the thread. The default is `auto`. If set to * `last_messages`, the thread will be truncated to the n most recent messages in the @@ -2737,6 +2943,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2985,6 +3218,26 @@ private constructor( 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 (completionTokens.asKnown().isPresent) 1 else 0) + + (if (promptTokens.asKnown().isPresent) 1 else 0) + + (if (totalTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt index 3bc7817ba..5d94026ef 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -2146,9 +2147,9 @@ private constructor( maxCompletionTokens() maxPromptTokens() metadata().ifPresent { it.validate() } - model() + model().ifPresent { it.validate() } parallelToolCalls() - reasoningEffort() + reasoningEffort().ifPresent { it.validate() } responseFormat().ifPresent { it.validate() } temperature() toolChoice().ifPresent { it.validate() } @@ -2158,6 +2159,39 @@ private constructor( 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 (assistantId.asKnown().isPresent) 1 else 0) + + (if (additionalInstructions.asKnown().isPresent) 1 else 0) + + (additionalMessages.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (if (maxCompletionTokens.asKnown().isPresent) 1 else 0) + + (if (maxPromptTokens.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + (if (parallelToolCalls.asKnown().isPresent) 1 else 0) + + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (toolChoice.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + + (truncationStrategy.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2456,12 +2490,33 @@ private constructor( } content().validate() - role() + role().validate() attachments().ifPresent { it.forEach { it.validate() } } metadata().ifPresent { it.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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (attachments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + /** The text contents of the message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -2500,14 +2555,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2530,6 +2584,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2600,17 +2682,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } @@ -2726,6 +2821,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2910,6 +3032,25 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + @JsonDeserialize(using = Tool.Deserializer::class) @JsonSerialize(using = Tool.Serializer::class) class Tool @@ -2935,13 +3076,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2972,6 +3112,38 @@ private constructor( 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 visitCodeInterpreter( + codeInterpreter: CodeInterpreterTool + ) = codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: JsonValue) = + fileSearch.let { + if (it == JsonValue.from(mapOf("type" to "file_search"))) 1 + else 0 + } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3034,17 +3206,14 @@ private constructor( when (type) { "code_interpreter" -> { - return Tool( - codeInterpreter = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(codeInterpreter = it, _json = json) } + ?: Tool(_json = json) } "file_search" -> { - return Tool( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Tool(fileSearch = it, _json = json) } + ?.takeIf { it.isValid() } ?: Tool(_json = json) } } @@ -3165,6 +3334,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3274,6 +3461,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3477,11 +3682,30 @@ private constructor( return@apply } - type() + type().validate() lastMessages() 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 = + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (lastMessages.asKnown().isPresent) 1 else 0) + /** * The truncation strategy to use for the thread. The default is `auto`. If set to * `last_messages`, the thread will be truncated to the n most recent messages in the @@ -3575,6 +3799,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt index 3b170c4c4..5883efe72 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.beta.threads.RunService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt index 8ec14fa58..560ddefcf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.beta.threads.RunServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt index ac46f8dd6..22d967142 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt @@ -374,6 +374,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunStatus.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunStatus.kt index 759babd50..e9aeccaf3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunStatus.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunStatus.kt @@ -136,6 +136,32 @@ class RunStatus @JsonCreator private constructor(private val value: JsonField !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogs.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogs.kt index 9dc012e3c..b0480fc28 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogs.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogs.kt @@ -209,6 +209,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + + (if (logs.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImage.kt index 00b2aa30e..af3b93863 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImage.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class CodeInterpreterOutputImage private constructor( @@ -205,6 +206,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("image")) 1 else 0 } + + (image.asKnown().getOrNull()?.validity() ?: 0) + class Image private constructor( private val fileId: JsonField, @@ -311,6 +331,22 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCall.kt index e61bf2e73..0c05404d7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCall.kt @@ -233,6 +233,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("code_interpreter")) 1 else 0 } + /** The Code Interpreter tool call definition. */ class CodeInterpreter private constructor( @@ -443,6 +462,25 @@ private constructor( 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 (input.asKnown().isPresent) 1 else 0) + + (outputs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** Text output from the Code Interpreter tool call as part of a run step. */ @JsonDeserialize(using = Output.Deserializer::class) @JsonSerialize(using = Output.Serializer::class) @@ -469,13 +507,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { logs != null -> visitor.visitLogs(logs) image != null -> visitor.visitImage(image) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -498,6 +535,32 @@ private constructor( 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 visitLogs(logs: LogsOutput) = logs.validity() + + override fun visitImage(image: ImageOutput) = image.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -557,16 +620,14 @@ private constructor( when (type) { "logs" -> { - return Output( - logs = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Output(logs = it, _json = json) + } ?: Output(_json = json) } "image" -> { - return Output( - image = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Output(image = it, _json = json) + } ?: Output(_json = json) } } @@ -759,6 +820,25 @@ private constructor( 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 (logs.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -942,6 +1022,25 @@ private constructor( 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 = + (image.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("image")) 1 else 0 } + class Image private constructor( private val fileId: JsonField, @@ -1080,6 +1179,23 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDelta.kt index 90db48e1d..c3f8068bf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDelta.kt @@ -264,6 +264,26 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("code_interpreter")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (codeInterpreter.asKnown().getOrNull()?.validity() ?: 0) + /** The Code Interpreter tool call definition. */ class CodeInterpreter private constructor( @@ -458,6 +478,25 @@ private constructor( 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 (input.asKnown().isPresent) 1 else 0) + + (outputs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** Text output from the Code Interpreter tool call as part of a run step. */ @JsonDeserialize(using = Output.Deserializer::class) @JsonSerialize(using = Output.Serializer::class) @@ -484,13 +523,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { logs != null -> visitor.visitLogs(logs) image != null -> visitor.visitImage(image) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -513,6 +551,33 @@ private constructor( 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 visitLogs(logs: CodeInterpreterLogs) = logs.validity() + + override fun visitImage(image: CodeInterpreterOutputImage) = + image.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -572,17 +637,15 @@ private constructor( when (type) { "logs" -> { - return Output( - logs = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Output(logs = it, _json = json) } ?: Output(_json = json) } "image" -> { - return Output( - image = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { Output(image = it, _json = json) } ?: Output(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCall.kt index d8e3726cf..a81fc2cce 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCall.kt @@ -18,6 +18,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class FileSearchToolCall private constructor( @@ -218,6 +219,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (fileSearch.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("file_search")) 1 else 0 } + /** For now, this is always going to be an empty object. */ class FileSearch private constructor( @@ -387,6 +407,25 @@ private constructor( 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 = + (rankingOptions.asKnown().getOrNull()?.validity() ?: 0) + + (results.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** The ranking options for the file search. */ class RankingOptions private constructor( @@ -564,11 +603,30 @@ private constructor( return@apply } - ranker() + ranker().validate() scoreThreshold() 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 = + (ranker.asKnown().getOrNull()?.validity() ?: 0) + + (if (scoreThreshold.asKnown().isPresent) 1 else 0) + /** * The ranker to use for the file search. If not specified will use the `auto` ranker. */ @@ -663,6 +721,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Ranker = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -959,6 +1044,27 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (if (fileName.asKnown().isPresent) 1 else 0) + + (if (score.asKnown().isPresent) 1 else 0) + + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class Content private constructor( private val text: JsonField, @@ -1100,10 +1206,29 @@ private constructor( } text() - type() + type().ifPresent { it.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 = + (if (text.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** The type of the content. */ class Type @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1191,6 +1316,34 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDelta.kt index 10af842bb..3d5bbab2d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDelta.kt @@ -221,6 +221,25 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_search")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCall.kt index 49e07774c..f9a1d5d0c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCall.kt @@ -212,6 +212,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (function.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + /** The definition of the function that was called. */ class Function private constructor( @@ -417,6 +436,26 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (output.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDelta.kt index 8788d216c..7651970b9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDelta.kt @@ -242,6 +242,26 @@ private constructor( 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 (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (function.asKnown().getOrNull()?.validity() ?: 0) + /** The definition of the function that was called. */ class Function private constructor( @@ -424,6 +444,26 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (output.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetails.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetails.kt index b3da21e4d..9630f53c4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetails.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetails.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Details of the message creation by the run step. */ class MessageCreationStepDetails @@ -181,6 +182,24 @@ private constructor( 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 = + (messageCreation.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("message_creation")) 1 else 0 } + class MessageCreation private constructor( private val messageId: JsonField, @@ -307,6 +326,22 @@ private constructor( 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 (messageId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStep.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStep.kt index 3d8697289..d93613530 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStep.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStep.kt @@ -818,14 +818,46 @@ private constructor( } } runId() - status() + status().validate() stepDetails().validate() threadId() - type() + type().validate() usage().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (if (assistantId.asKnown().isPresent) 1 else 0) + + (if (cancelledAt.asKnown().isPresent) 1 else 0) + + (if (completedAt.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (expiredAt.asKnown().isPresent) 1 else 0) + + (if (failedAt.asKnown().isPresent) 1 else 0) + + (lastError.asKnown().getOrNull()?.validity() ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("thread.run.step")) 1 else 0 } + + (if (runId.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (stepDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (threadId.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + /** The last error associated with this run step. Will be `null` if there are no errors. */ class LastError private constructor( @@ -981,11 +1013,30 @@ private constructor( return@apply } - code() + code().validate() message() 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 = + (code.asKnown().getOrNull()?.validity() ?: 0) + + (if (message.asKnown().isPresent) 1 else 0) + /** One of `server_error` or `rate_limit_exceeded`. */ class Code @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1074,6 +1125,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Code = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1179,6 +1257,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1303,6 +1399,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1346,13 +1469,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { messageCreation != null -> visitor.visitMessageCreation(messageCreation) toolCalls != null -> visitor.visitToolCalls(toolCalls) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1375,6 +1497,34 @@ private constructor( 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 visitMessageCreation(messageCreation: MessageCreationStepDetails) = + messageCreation.validity() + + override fun visitToolCalls(toolCalls: ToolCallsStepDetails) = + toolCalls.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1440,17 +1590,14 @@ private constructor( when (type) { "message_creation" -> { - return StepDetails( - messageCreation = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { StepDetails(messageCreation = it, _json = json) } + ?: StepDetails(_json = json) } "tool_calls" -> { - return StepDetails( - toolCalls = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + StepDetails(toolCalls = it, _json = json) + } ?: StepDetails(_json = json) } } @@ -1561,6 +1708,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1791,6 +1965,26 @@ private constructor( 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 (completionTokens.asKnown().isPresent) 1 else 0) + + (if (promptTokens.asKnown().isPresent) 1 else 0) + + (if (totalTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDelta.kt index 8cbea679e..07dd82fd3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDelta.kt @@ -150,6 +150,22 @@ private constructor( 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 = (stepDetails.asKnown().getOrNull()?.validity() ?: 0) + /** The details of the run step. */ @JsonDeserialize(using = StepDetails.Deserializer::class) @JsonSerialize(using = StepDetails.Serializer::class) @@ -180,13 +196,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { messageCreation != null -> visitor.visitMessageCreation(messageCreation) toolCalls != null -> visitor.visitToolCalls(toolCalls) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -209,6 +224,34 @@ private constructor( 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 visitMessageCreation(messageCreation: RunStepDeltaMessageDelta) = + messageCreation.validity() + + override fun visitToolCalls(toolCalls: ToolCallDeltaObject) = + toolCalls.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -274,17 +317,14 @@ private constructor( when (type) { "message_creation" -> { - return StepDetails( - messageCreation = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { StepDetails(messageCreation = it, _json = json) } + ?: StepDetails(_json = json) } "tool_calls" -> { - return StepDetails( - toolCalls = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + StepDetails(toolCalls = it, _json = json) + } ?: StepDetails(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEvent.kt index a3853ab18..b1dfbcaa1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Represents a run step delta i.e. any changed fields on a run step during streaming. */ class RunStepDeltaEvent @@ -211,6 +212,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (delta.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("thread.run.step.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDelta.kt index 554998019..ebcc54b69 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDelta.kt @@ -14,6 +14,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Details of the message creation by the run step. */ class RunStepDeltaMessageDelta @@ -164,6 +165,24 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("message_creation")) 1 else 0 } + + (messageCreation.asKnown().getOrNull()?.validity() ?: 0) + class MessageCreation private constructor( private val messageId: JsonField, @@ -273,6 +292,22 @@ private constructor( 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 (messageId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepInclude.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepInclude.kt index 5e814f53a..eeb272172 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepInclude.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/RunStepInclude.kt @@ -90,6 +90,32 @@ class RunStepInclude @JsonCreator private constructor(private val value: JsonFie fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): RunStepInclude = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt index 799b92256..86bed20d4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.beta.threads.runs.StepService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt index 59645a2ee..fbc8d0267 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.beta.threads.runs.StepServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt index 4e2f97202..ca18dea9b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt @@ -427,6 +427,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCall.kt index 8fcf0f6b7..c1f577ace 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCall.kt @@ -51,14 +51,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -85,6 +84,34 @@ private constructor( 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 visitCodeInterpreter(codeInterpreter: CodeInterpreterToolCall) = + codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: FileSearchToolCall) = fileSearch.validity() + + override fun visitFunction(function: FunctionToolCall) = function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -149,23 +176,19 @@ private constructor( when (type) { "code_interpreter" -> { - return ToolCall( - codeInterpreter = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ToolCall(codeInterpreter = it, _json = json) + } ?: ToolCall(_json = json) } "file_search" -> { - return ToolCall( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ToolCall(fileSearch = it, _json = json) + } ?: ToolCall(_json = json) } "function" -> { - return ToolCall( - function = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ToolCall(function = it, _json = json) + } ?: ToolCall(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDelta.kt index c8fb5b75d..b16e20838 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDelta.kt @@ -53,14 +53,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { codeInterpreter != null -> visitor.visitCodeInterpreter(codeInterpreter) fileSearch != null -> visitor.visitFileSearch(fileSearch) function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -87,6 +86,35 @@ private constructor( 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 visitCodeInterpreter(codeInterpreter: CodeInterpreterToolCallDelta) = + codeInterpreter.validity() + + override fun visitFileSearch(fileSearch: FileSearchToolCallDelta) = + fileSearch.validity() + + override fun visitFunction(function: FunctionToolCallDelta) = function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -155,23 +183,19 @@ private constructor( when (type) { "code_interpreter" -> { - return ToolCallDelta( - codeInterpreter = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ToolCallDelta(codeInterpreter = it, _json = json) } + ?: ToolCallDelta(_json = json) } "file_search" -> { - return ToolCallDelta( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ToolCallDelta(fileSearch = it, _json = json) + } ?: ToolCallDelta(_json = json) } "function" -> { - return ToolCallDelta( - function = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ToolCallDelta(function = it, _json = json) + } ?: ToolCallDelta(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObject.kt index 99e696661..371402d8b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObject.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Details of the tool call. */ class ToolCallDeltaObject @@ -224,6 +225,24 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("tool_calls")) 1 else 0 } + + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetails.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetails.kt index 875147aba..a39227b34 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetails.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetails.kt @@ -16,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Details of the tool call. */ class ToolCallsStepDetails @@ -212,6 +213,24 @@ private constructor( 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 = + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + type.let { if (it == JsonValue.from("tool_calls")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt index c2d29e45e..d0b068350 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt @@ -431,12 +431,36 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - serviceTier() + serviceTier().ifPresent { it.validate() } systemFingerprint() usage().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (created.asKnown().isPresent) 1 else 0) + + (if (model.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("chat.completion")) 1 else 0 } + + (serviceTier.asKnown().getOrNull()?.validity() ?: 0) + + (if (systemFingerprint.asKnown().isPresent) 1 else 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + class Choice private constructor( private val finishReason: JsonField, @@ -687,13 +711,34 @@ private constructor( return@apply } - finishReason() + finishReason().validate() index() logprobs().ifPresent { it.validate() } message().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 = + (finishReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (index.asKnown().isPresent) 1 else 0) + + (logprobs.asKnown().getOrNull()?.validity() ?: 0) + + (message.asKnown().getOrNull()?.validity() ?: 0) + /** * The reason the model stopped generating tokens. This will be `stop` if the model hit a * natural stop point or a provided stop sequence, `length` if the maximum number of tokens @@ -810,6 +855,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): FinishReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1034,6 +1106,25 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (refusal.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1159,6 +1250,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ServiceTier = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParam.kt index 10c18b1a0..734fb5ed6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParam.kt @@ -19,6 +19,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -416,6 +417,29 @@ private constructor( 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 = + role.let { if (it == JsonValue.from("assistant")) 1 else 0 } + + (audio.asKnown().getOrNull()?.validity() ?: 0) + + (content.asKnown().getOrNull()?.validity() ?: 0) + + (functionCall.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (refusal.asKnown().isPresent) 1 else 0) + + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** * Data about a previous audio response from the model. * [Learn more](https://platform.openai.com/docs/guides/audio). @@ -540,6 +564,22 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -598,13 +638,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -627,6 +666,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -696,20 +763,32 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef< + List + >(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize( - node, - jacksonTypeRef>(), - ) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } @@ -762,13 +841,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) refusal != null -> visitor.visitRefusal(refusal) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -791,6 +869,34 @@ private constructor( 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 visitText(text: ChatCompletionContentPartText) = + text.validity() + + override fun visitRefusal(refusal: ChatCompletionContentPartRefusal) = + refusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -874,24 +980,28 @@ private constructor( when (type) { "text" -> { - return ChatCompletionRequestAssistantMessageContentPart( - text = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ChatCompletionRequestAssistantMessageContentPart( + text = it, + _json = json, + ) + } ?: ChatCompletionRequestAssistantMessageContentPart(_json = json) } "refusal" -> { - return ChatCompletionRequestAssistantMessageContentPart( - refusal = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ChatCompletionRequestAssistantMessageContentPart( + refusal = it, + _json = json, + ) + } ?: ChatCompletionRequestAssistantMessageContentPart(_json = json) } } @@ -1097,6 +1207,24 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudio.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudio.kt index 327c0177e..ebeed39e3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudio.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudio.kt @@ -252,6 +252,26 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (data.asKnown().isPresent) 1 else 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (transcript.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParam.kt index 93f03b9f0..4f1275c96 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParam.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * Parameters for audio output. Required when audio output is requested with `modalities: @@ -187,11 +188,28 @@ private constructor( return@apply } - format() + format().validate() voice() 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 = + (format.asKnown().getOrNull()?.validity() ?: 0) + (if (voice.asKnown().isPresent) 1 else 0) + /** * Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. */ @@ -298,6 +316,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Format = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -454,6 +499,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Voice = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionChunk.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionChunk.kt index 6a0367671..8d5f551ef 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionChunk.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionChunk.kt @@ -457,12 +457,36 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - serviceTier() + serviceTier().ifPresent { it.validate() } systemFingerprint() usage().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (created.asKnown().isPresent) 1 else 0) + + (if (model.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("chat.completion.chunk")) 1 else 0 } + + (serviceTier.asKnown().getOrNull()?.validity() ?: 0) + + (if (systemFingerprint.asKnown().isPresent) 1 else 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + class Choice private constructor( private val delta: JsonField, @@ -712,12 +736,33 @@ private constructor( } delta().validate() - finishReason() + finishReason().ifPresent { it.validate() } index() logprobs().ifPresent { it.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 = + (delta.asKnown().getOrNull()?.validity() ?: 0) + + (finishReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (index.asKnown().isPresent) 1 else 0) + + (logprobs.asKnown().getOrNull()?.validity() ?: 0) + /** A chat completion delta generated by streamed model responses. */ class Delta private constructor( @@ -1003,11 +1048,33 @@ private constructor( content() functionCall().ifPresent { it.validate() } refusal() - role() + role().ifPresent { it.validate() } toolCalls().ifPresent { it.forEach { it.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 = + (if (content.asKnown().isPresent) 1 else 0) + + (functionCall.asKnown().getOrNull()?.validity() ?: 0) + + (if (refusal.asKnown().isPresent) 1 else 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** * Deprecated and replaced by `tool_calls`. The name and arguments of a function that * should be called, as generated by the model. @@ -1172,6 +1239,25 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1299,6 +1385,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1539,10 +1652,31 @@ private constructor( index() id() function().ifPresent { it.validate() } - type() + type().ifPresent { it.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 = + (if (index.asKnown().isPresent) 1 else 0) + + (if (id.asKnown().isPresent) 1 else 0) + + (function.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + class Function private constructor( private val arguments: JsonField, @@ -1705,6 +1839,25 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1810,6 +1963,34 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1975,6 +2156,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): FinishReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2199,6 +2407,25 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (refusal.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2324,6 +2551,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ServiceTier = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPart.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPart.kt index 7c3e781b7..695ada3ee 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPart.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPart.kt @@ -78,15 +78,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) imageUrl != null -> visitor.visitImageUrl(imageUrl) inputAudio != null -> visitor.visitInputAudio(inputAudio) file != null -> visitor.visitFile(file) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -117,6 +116,37 @@ private constructor( 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 visitText(text: ChatCompletionContentPartText) = text.validity() + + override fun visitImageUrl(imageUrl: ChatCompletionContentPartImage) = + imageUrl.validity() + + override fun visitInputAudio(inputAudio: ChatCompletionContentPartInputAudio) = + inputAudio.validity() + + override fun visitFile(file: File) = file.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -205,33 +235,27 @@ private constructor( when (type) { "text" -> { - return ChatCompletionContentPart( - text = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChatCompletionContentPart(text = it, _json = json) } + ?: ChatCompletionContentPart(_json = json) } "image_url" -> { - return ChatCompletionContentPart( - imageUrl = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChatCompletionContentPart(imageUrl = it, _json = json) } + ?: ChatCompletionContentPart(_json = json) } "input_audio" -> { - return ChatCompletionContentPart( - inputAudio = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ChatCompletionContentPart(inputAudio = it, _json = json) } + ?: ChatCompletionContentPart(_json = json) } "file" -> { - return ChatCompletionContentPart( - file = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ChatCompletionContentPart(file = it, _json = json) + } ?: ChatCompletionContentPart(_json = json) } } @@ -415,6 +439,25 @@ private constructor( 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 = + (file.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("file")) 1 else 0 } + class FileObject private constructor( private val fileData: JsonField, @@ -601,6 +644,26 @@ private constructor( 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 (fileData.asKnown().isPresent) 1 else 0) + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImage.kt index 1050211e6..2d6b02440 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImage.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Learn about [image inputs](https://platform.openai.com/docs/guides/vision). */ class ChatCompletionContentPartImage @@ -178,6 +179,24 @@ private constructor( 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 = + (imageUrl.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("image_url")) 1 else 0 } + class ImageUrl private constructor( private val url: JsonField, @@ -331,10 +350,29 @@ private constructor( } url() - detail() + detail().ifPresent { it.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 = + (if (url.asKnown().isPresent) 1 else 0) + + (detail.asKnown().getOrNull()?.validity() ?: 0) + /** * Specifies the detail level of the image. Learn more in the * [Vision guide](https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding). @@ -434,6 +472,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Detail = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudio.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudio.kt index c6b1d1a32..908a78e08 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudio.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudio.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Learn about [audio inputs](https://platform.openai.com/docs/guides/audio). */ class ChatCompletionContentPartInputAudio @@ -183,6 +184,24 @@ private constructor( 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 = + (inputAudio.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("input_audio")) 1 else 0 } + class InputAudio private constructor( private val data: JsonField, @@ -338,10 +357,29 @@ private constructor( } data() - format() + format().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 = + (if (data.asKnown().isPresent) 1 else 0) + + (format.asKnown().getOrNull()?.validity() ?: 0) + /** The format of the encoded audio data. Currently supports "wav" and "mp3". */ class Format @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -432,6 +470,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Format = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusal.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusal.kt index 7067d3fa9..af2c823fb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusal.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusal.kt @@ -178,6 +178,24 @@ private constructor( 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 (refusal.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("refusal")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartText.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartText.kt index 46a2863f0..a3a51bbaf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartText.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartText.kt @@ -177,6 +177,24 @@ private constructor( 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("text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt index 8f8aca34c..4a5b85309 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -3652,7 +3653,7 @@ private constructor( } messages().forEach { it.validate() } - model() + model().validate() audio().ifPresent { it.validate() } frequencyPenalty() functionCall().ifPresent { it.validate() } @@ -3662,15 +3663,15 @@ private constructor( maxCompletionTokens() maxTokens() metadata().ifPresent { it.validate() } - modalities() + modalities().ifPresent { it.forEach { it.validate() } } n() parallelToolCalls() prediction().ifPresent { it.validate() } presencePenalty() - reasoningEffort() + reasoningEffort().ifPresent { it.validate() } responseFormat().ifPresent { it.validate() } seed() - serviceTier() + serviceTier().ifPresent { it.validate() } stop().ifPresent { it.validate() } store() streamOptions().ifPresent { it.validate() } @@ -3684,6 +3685,53 @@ private constructor( 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 = + (messages.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + (audio.asKnown().getOrNull()?.validity() ?: 0) + + (if (frequencyPenalty.asKnown().isPresent) 1 else 0) + + (functionCall.asKnown().getOrNull()?.validity() ?: 0) + + (functions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (logitBias.asKnown().getOrNull()?.validity() ?: 0) + + (if (logprobs.asKnown().isPresent) 1 else 0) + + (if (maxCompletionTokens.asKnown().isPresent) 1 else 0) + + (if (maxTokens.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (modalities.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (n.asKnown().isPresent) 1 else 0) + + (if (parallelToolCalls.asKnown().isPresent) 1 else 0) + + (prediction.asKnown().getOrNull()?.validity() ?: 0) + + (if (presencePenalty.asKnown().isPresent) 1 else 0) + + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (seed.asKnown().isPresent) 1 else 0) + + (serviceTier.asKnown().getOrNull()?.validity() ?: 0) + + (stop.asKnown().getOrNull()?.validity() ?: 0) + + (if (store.asKnown().isPresent) 1 else 0) + + (streamOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (toolChoice.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topLogprobs.asKnown().isPresent) 1 else 0) + + (if (topP.asKnown().isPresent) 1 else 0) + + (if (user.asKnown().isPresent) 1 else 0) + + (webSearchOptions.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3759,13 +3807,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { mode != null -> visitor.visitMode(mode) functionCallOption != null -> visitor.visitFunctionCallOption(functionCallOption) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3776,7 +3823,9 @@ private constructor( accept( object : Visitor { - override fun visitMode(mode: FunctionCallMode) {} + override fun visitMode(mode: FunctionCallMode) { + mode.validate() + } override fun visitFunctionCallOption( functionCallOption: ChatCompletionFunctionCallOption @@ -3788,6 +3837,34 @@ private constructor( 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 visitMode(mode: FunctionCallMode) = mode.validity() + + override fun visitFunctionCallOption( + functionCallOption: ChatCompletionFunctionCallOption + ) = functionCallOption.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3861,17 +3938,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): FunctionCall { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return FunctionCall(mode = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + FunctionCall(mode = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { FunctionCall(functionCallOption = 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 -> FunctionCall(_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() } - tryDeserialize(node, jacksonTypeRef()) { - it.validate() - } - ?.let { - return FunctionCall(functionCallOption = it, _json = json) - } - - return FunctionCall(_json = json) } } @@ -3989,6 +4076,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): FunctionCallMode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4228,6 +4342,26 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + + (if (description.asKnown().isPresent) 1 else 0) + + (parameters.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4321,6 +4455,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4412,6 +4564,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4514,6 +4684,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Modality = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4588,14 +4785,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) jsonSchema != null -> visitor.visitJsonSchema(jsonSchema) jsonObject != null -> visitor.visitJsonObject(jsonObject) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4622,6 +4818,36 @@ private constructor( 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 visitText(text: ResponseFormatText) = text.validity() + + override fun visitJsonSchema(jsonSchema: ResponseFormatJsonSchema) = + jsonSchema.validity() + + override fun visitJsonObject(jsonObject: ResponseFormatJsonObject) = + jsonObject.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4708,20 +4934,31 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): ResponseFormat { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseFormat(text = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseFormat(jsonSchema = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseFormat(jsonObject = it, _json = json) - } - - return ResponseFormat(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFormat(text = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFormat(jsonSchema = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFormat(jsonObject = 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 boolean). + 0 -> ResponseFormat(_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() + } } } @@ -4844,6 +5081,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ServiceTier = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4884,13 +5148,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) strings != null -> visitor.visitStrings(strings) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4909,6 +5172,32 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4960,14 +5249,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Stop { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Stop(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Stop(strings = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Stop(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Stop(strings = 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 object). + 0 -> Stop(_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() } - - return Stop(_json = json) } } @@ -5156,11 +5459,30 @@ private constructor( return@apply } - searchContextSize() + searchContextSize().ifPresent { it.validate() } userLocation().ifPresent { it.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 = + (searchContextSize.asKnown().getOrNull()?.validity() ?: 0) + + (userLocation.asKnown().getOrNull()?.validity() ?: 0) + /** * High level guidance for the amount of context window space to use for the search. One of * `low`, `medium`, or `high`. `medium` is the default. @@ -5264,6 +5586,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): SearchContextSize = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -5450,6 +5799,25 @@ private constructor( 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 = + (approximate.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("approximate")) 1 else 0 } + /** Approximate location parameters for the search. */ class Approximate private constructor( @@ -5687,6 +6055,27 @@ private constructor( 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 (city.asKnown().isPresent) 1 else 0) + + (if (country.asKnown().isPresent) 1 else 0) + + (if (region.asKnown().isPresent) 1 else 0) + + (if (timezone.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleted.kt index 31146400a..ffaaa8dca 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleted.kt @@ -209,6 +209,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("chat.completion.deleted")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParam.kt index 2ad313460..2fec4a122 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParam.kt @@ -19,12 +19,14 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Developer-provided instructions that the model should follow, regardless of messages sent by the @@ -239,6 +241,25 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("developer")) 1 else 0 } + + (if (name.asKnown().isPresent) 1 else 0) + /** The contents of the developer message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -275,13 +296,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -304,6 +324,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -372,17 +420,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOption.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOption.kt index 7992ad10b..3df5015d7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOption.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOption.kt @@ -145,6 +145,21 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParam.kt index 8eb317c96..55193eddf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParam.kt @@ -218,6 +218,25 @@ private constructor( 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 (content.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + role.let { if (it == JsonValue.from("function")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt index c29aaad17..fc979a940 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.chat.ChatCompletionService import java.util.Collections import java.util.Objects @@ -120,6 +121,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt index cfa24163a..691d46131 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.chat.ChatCompletionServiceAsync import java.util.Collections import java.util.Objects @@ -122,6 +123,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListParams.kt index b577c8c5c..6b7c6ef05 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListParams.kt @@ -458,6 +458,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessage.kt index 8169e2f16..28ef9e25f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessage.kt @@ -439,6 +439,29 @@ private constructor( 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 (content.asKnown().isPresent) 1 else 0) + + (if (refusal.asKnown().isPresent) 1 else 0) + + role.let { if (it == JsonValue.from("assistant")) 1 else 0 } + + (annotations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (audio.asKnown().getOrNull()?.validity() ?: 0) + + (functionCall.asKnown().getOrNull()?.validity() ?: 0) + + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** A URL citation when using web search. */ class Annotation private constructor( @@ -607,6 +630,25 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("url_citation")) 1 else 0 } + + (urlCitation.asKnown().getOrNull()?.validity() ?: 0) + /** A URL citation when using web search. */ class UrlCitation private constructor( @@ -852,6 +894,27 @@ private constructor( 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 (endIndex.asKnown().isPresent) 1 else 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (title.asKnown().isPresent) 1 else 0) + + (if (url.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1062,6 +1125,24 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParam.kt index 53c72211b..8e27021c1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParam.kt @@ -97,8 +97,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { developer != null -> visitor.visitDeveloper(developer) system != null -> visitor.visitSystem(system) user != null -> visitor.visitUser(user) @@ -107,7 +107,6 @@ private constructor( function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -146,6 +145,43 @@ private constructor( 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 visitDeveloper(developer: ChatCompletionDeveloperMessageParam) = + developer.validity() + + override fun visitSystem(system: ChatCompletionSystemMessageParam) = + system.validity() + + override fun visitUser(user: ChatCompletionUserMessageParam) = user.validity() + + override fun visitAssistant(assistant: ChatCompletionAssistantMessageParam) = + assistant.validity() + + override fun visitTool(tool: ChatCompletionToolMessageParam) = tool.validity() + + override fun visitFunction(function: ChatCompletionFunctionMessageParam) = + function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -258,50 +294,43 @@ private constructor( when (role) { "developer" -> { - return ChatCompletionMessageParam( - developer = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ChatCompletionMessageParam(developer = it, _json = json) } + ?: ChatCompletionMessageParam(_json = json) } "system" -> { - return ChatCompletionMessageParam( - system = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChatCompletionMessageParam(system = it, _json = json) } + ?: ChatCompletionMessageParam(_json = json) } "user" -> { - return ChatCompletionMessageParam( - user = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChatCompletionMessageParam(user = it, _json = json) } + ?: ChatCompletionMessageParam(_json = json) } "assistant" -> { - return ChatCompletionMessageParam( - assistant = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ChatCompletionMessageParam(assistant = it, _json = json) } + ?: ChatCompletionMessageParam(_json = json) } "tool" -> { - return ChatCompletionMessageParam( - tool = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ChatCompletionMessageParam(tool = it, _json = json) } + ?: ChatCompletionMessageParam(_json = json) } "function" -> { - return ChatCompletionMessageParam( - function = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ChatCompletionMessageParam(function = it, _json = json) } + ?: ChatCompletionMessageParam(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCall.kt index 47a5dbf5a..d240a9d9e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCall.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class ChatCompletionMessageToolCall private constructor( @@ -211,6 +212,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (function.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + /** The function that the model called. */ class Function private constructor( @@ -381,6 +401,24 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionModality.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionModality.kt index 331f18f2c..8eff2b099 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionModality.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionModality.kt @@ -95,6 +95,32 @@ private constructor(private val value: JsonField) : Enum { fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ChatCompletionModality = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoice.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoice.kt index 1a57c1cb5..debb05de7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoice.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoice.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Specifies a tool the model should use. Use to force the model to call a specific function. */ class ChatCompletionNamedToolChoice @@ -175,6 +176,24 @@ private constructor( 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 = + (function.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + class Function private constructor( private val name: JsonField, @@ -296,6 +315,22 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContent.kt index 4141ea73d..84af8bb81 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContent.kt @@ -19,12 +19,14 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Static predicted output content, such as the content of a text file that is being regenerated. @@ -206,6 +208,24 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("content")) 1 else 0 } + /** * The content that should be matched when generating a model response. If generated tokens * would match this content, the entire model response can be returned much more quickly. @@ -253,13 +273,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -282,6 +301,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -358,17 +405,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRole.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRole.kt index 7e911267a..797c0895c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRole.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRole.kt @@ -119,6 +119,32 @@ class ChatCompletionRole @JsonCreator private constructor(private val value: Jso fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ChatCompletionRole = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessage.kt index 6292c00f6..cb7aefaa4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessage.kt @@ -476,6 +476,30 @@ private constructor( 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 (content.asKnown().isPresent) 1 else 0) + + (if (refusal.asKnown().isPresent) 1 else 0) + + role.let { if (it == JsonValue.from("assistant")) 1 else 0 } + + (annotations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (audio.asKnown().getOrNull()?.validity() ?: 0) + + (functionCall.asKnown().getOrNull()?.validity() ?: 0) + + (toolCalls.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (id.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptions.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptions.kt index 77a6855ce..a1b051f7e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptions.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptions.kt @@ -146,6 +146,21 @@ private constructor( 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 (includeUsage.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParam.kt index fa52af8b6..d61528826 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParam.kt @@ -19,12 +19,14 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Developer-provided instructions that the model should follow, regardless of messages sent by the @@ -238,6 +240,25 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("system")) 1 else 0 } + + (if (name.asKnown().isPresent) 1 else 0) + /** The contents of the system message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -274,13 +295,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -303,6 +323,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -371,17 +419,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprob.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprob.kt index d8f247d5b..6484c3a9f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprob.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprob.kt @@ -295,6 +295,26 @@ private constructor( 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 (token.asKnown().isPresent) 1 else 0) + + (bytes.asKnown().getOrNull()?.size ?: 0) + + (if (logprob.asKnown().isPresent) 1 else 0) + + (topLogprobs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class TopLogprob private constructor( private val token: JsonField, @@ -518,6 +538,26 @@ private constructor( 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 (token.asKnown().isPresent) 1 else 0) + + (bytes.asKnown().getOrNull()?.size ?: 0) + + (if (logprob.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTool.kt index 2eb1bb124..b466ca9ab 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionTool.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import com.openai.models.FunctionDefinition import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class ChatCompletionTool private constructor( @@ -178,6 +179,24 @@ private constructor( 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 = + (function.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOption.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOption.kt index 522772d17..acbe8983c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOption.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOption.kt @@ -15,6 +15,7 @@ import com.openai.core.BaseSerializer import com.openai.core.Enum import com.openai.core.JsonField import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -70,13 +71,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) namedToolChoice != null -> visitor.visitNamedToolChoice(namedToolChoice) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -87,7 +87,9 @@ private constructor( accept( object : Visitor { - override fun visitAuto(auto: Auto) {} + override fun visitAuto(auto: Auto) { + auto.validate() + } override fun visitNamedToolChoice(namedToolChoice: ChatCompletionNamedToolChoice) { namedToolChoice.validate() @@ -97,6 +99,32 @@ private constructor( 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 visitAuto(auto: Auto) = auto.validity() + + override fun visitNamedToolChoice(namedToolChoice: ChatCompletionNamedToolChoice) = + namedToolChoice.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -174,15 +202,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): ChatCompletionToolChoiceOption { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return ChatCompletionToolChoiceOption(auto = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ChatCompletionToolChoiceOption(auto = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ChatCompletionToolChoiceOption(namedToolChoice = 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 -> ChatCompletionToolChoiceOption(_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() } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ChatCompletionToolChoiceOption(namedToolChoice = it, _json = json) - } - - return ChatCompletionToolChoiceOption(_json = json) } } @@ -299,6 +339,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Auto = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParam.kt index aa7b6a269..01df56d89 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParam.kt @@ -19,12 +19,14 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ChatCompletionToolMessageParam private constructor( @@ -234,6 +236,25 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("tool")) 1 else 0 } + + (if (toolCallId.asKnown().isPresent) 1 else 0) + /** The contents of the tool message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -270,13 +291,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -299,6 +319,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -367,17 +415,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt index 9c598491f..3e22c07f4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt @@ -407,6 +407,23 @@ private constructor( 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 = (metadata.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -499,6 +516,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParam.kt index 46eff29fa..55f9c1b28 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParam.kt @@ -19,12 +19,14 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Messages sent by an end user, containing prompts or additional context information. */ class ChatCompletionUserMessageParam @@ -234,6 +236,25 @@ private constructor( 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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("user")) 1 else 0 } + + (if (name.asKnown().isPresent) 1 else 0) + /** The contents of the user message. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -272,13 +293,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) arrayOfContentParts != null -> visitor.visitArrayOfContentParts(arrayOfContentParts) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -301,6 +321,34 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitArrayOfContentParts( + arrayOfContentParts: List + ) = arrayOfContentParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -369,17 +417,27 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(text = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Content(arrayOfContentParts = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(arrayOfContentParts = it, _json = json) - } - - return Content(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt index 51d6f0554..4382869a6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.chat.completions.ChatCompletionStoreMessage import com.openai.services.blocking.chat.completions.MessageService import java.util.Collections @@ -120,6 +121,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt index 946d54aa9..01873ce17 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.chat.completions.ChatCompletionStoreMessage import com.openai.services.async.chat.completions.MessageServiceAsync import java.util.Collections @@ -125,6 +126,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt index 182d726af..0f7c3f25f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt @@ -339,6 +339,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/completions/Completion.kt b/openai-java-core/src/main/kotlin/com/openai/models/completions/Completion.kt index a6e756736..4d1c791b4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/completions/Completion.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/completions/Completion.kt @@ -17,6 +17,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Represents a completion response from the API. Note: both the streamed and non-streamed response @@ -386,6 +387,29 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (created.asKnown().isPresent) 1 else 0) + + (if (model.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("text_completion")) 1 else 0 } + + (if (systemFingerprint.asKnown().isPresent) 1 else 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionChoice.kt b/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionChoice.kt index c4317c7ef..c62061ac3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionChoice.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionChoice.kt @@ -248,13 +248,33 @@ private constructor( return@apply } - finishReason() + finishReason().validate() index() logprobs().ifPresent { it.validate() } text() 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 = + (finishReason.asKnown().getOrNull()?.validity() ?: 0) + + (if (index.asKnown().isPresent) 1 else 0) + + (logprobs.asKnown().getOrNull()?.validity() ?: 0) + + (if (text.asKnown().isPresent) 1 else 0) + /** * The reason the model stopped generating tokens. This will be `stop` if the model hit a * natural stop point or a provided stop sequence, `length` if the maximum number of tokens @@ -355,6 +375,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): FinishReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -640,6 +687,27 @@ private constructor( 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 = + (textOffset.asKnown().getOrNull()?.size ?: 0) + + (tokenLogprobs.asKnown().getOrNull()?.size ?: 0) + + (tokens.asKnown().getOrNull()?.size ?: 0) + + (topLogprobs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + class TopLogprob @JsonCreator private constructor( @@ -709,6 +777,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionCreateParams.kt index 33da69f99..49ba51832 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -2026,6 +2027,40 @@ private constructor( 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 (model.asKnown().isPresent) 1 else 0) + + (prompt.asKnown().getOrNull()?.validity() ?: 0) + + (if (bestOf.asKnown().isPresent) 1 else 0) + + (if (echo.asKnown().isPresent) 1 else 0) + + (if (frequencyPenalty.asKnown().isPresent) 1 else 0) + + (logitBias.asKnown().getOrNull()?.validity() ?: 0) + + (if (logprobs.asKnown().isPresent) 1 else 0) + + (if (maxTokens.asKnown().isPresent) 1 else 0) + + (if (n.asKnown().isPresent) 1 else 0) + + (if (presencePenalty.asKnown().isPresent) 1 else 0) + + (if (seed.asKnown().isPresent) 1 else 0) + + (stop.asKnown().getOrNull()?.validity() ?: 0) + + (streamOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (suffix.asKnown().isPresent) 1 else 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (if (topP.asKnown().isPresent) 1 else 0) + + (if (user.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2141,6 +2176,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Model = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2200,15 +2262,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) arrayOfStrings != null -> visitor.visitArrayOfStrings(arrayOfStrings) arrayOfTokens != null -> visitor.visitArrayOfTokens(arrayOfTokens) arrayOfTokenArrays != null -> visitor.visitArrayOfTokenArrays(arrayOfTokenArrays) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2231,6 +2292,38 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitArrayOfStrings(arrayOfStrings: List) = + arrayOfStrings.size + + override fun visitArrayOfTokens(arrayOfTokens: List) = arrayOfTokens.size + + override fun visitArrayOfTokenArrays(arrayOfTokenArrays: List>) = + arrayOfTokenArrays.sumOf { it.size.toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2298,20 +2391,34 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Prompt { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Prompt(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Prompt(arrayOfStrings = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Prompt(arrayOfTokens = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>>())?.let { - return Prompt(arrayOfTokenArrays = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Prompt(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Prompt(arrayOfStrings = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Prompt(arrayOfTokens = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>>())?.let { + Prompt(arrayOfTokenArrays = 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 object). + 0 -> Prompt(_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() } - - return Prompt(_json = json) } } @@ -2414,6 +2521,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2458,13 +2583,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) strings != null -> visitor.visitStrings(strings) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2483,6 +2607,32 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2534,14 +2684,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Stop { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Stop(string = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Stop(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Stop(strings = 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 object). + 0 -> Stop(_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() } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Stop(strings = it, _json = json) - } - - return Stop(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionUsage.kt b/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionUsage.kt index f3cc47e8b..52e76afc3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionUsage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/completions/CompletionUsage.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Usage statistics for the completion request. */ class CompletionUsage @@ -317,6 +318,27 @@ private constructor( 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 (completionTokens.asKnown().isPresent) 1 else 0) + + (if (promptTokens.asKnown().isPresent) 1 else 0) + + (if (totalTokens.asKnown().isPresent) 1 else 0) + + (completionTokensDetails.asKnown().getOrNull()?.validity() ?: 0) + + (promptTokensDetails.asKnown().getOrNull()?.validity() ?: 0) + /** Breakdown of tokens used in a completion. */ class CompletionTokensDetails private constructor( @@ -578,6 +600,27 @@ private constructor( 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 (acceptedPredictionTokens.asKnown().isPresent) 1 else 0) + + (if (audioTokens.asKnown().isPresent) 1 else 0) + + (if (reasoningTokens.asKnown().isPresent) 1 else 0) + + (if (rejectedPredictionTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -749,6 +792,25 @@ private constructor( 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 (audioTokens.asKnown().isPresent) 1 else 0) + + (if (cachedTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/CreateEmbeddingResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/CreateEmbeddingResponse.kt index 00cb7864c..c3707c0fa 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/CreateEmbeddingResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/CreateEmbeddingResponse.kt @@ -16,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class CreateEmbeddingResponse private constructor( @@ -260,6 +261,26 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("list")) 1 else 0 } + + (usage.asKnown().getOrNull()?.validity() ?: 0) + /** The usage information for the request. */ class Usage private constructor( @@ -431,6 +452,25 @@ private constructor( 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 (promptTokens.asKnown().isPresent) 1 else 0) + + (if (totalTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/Embedding.kt b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/Embedding.kt index e910ded46..0feb01e98 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/Embedding.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/Embedding.kt @@ -16,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Represents an embedding vector returned by embedding endpoint. */ class Embedding @@ -234,6 +235,25 @@ private constructor( 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 = + (embedding.asKnown().getOrNull()?.size ?: 0) + + (if (index.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("embedding")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt index c9e3aa1e6..075ffd2b1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -29,6 +30,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Creates an embedding vector representing the input text. */ class EmbeddingCreateParams @@ -741,13 +743,35 @@ private constructor( } input().validate() - model() + model().validate() dimensions() - encodingFormat() + encodingFormat().ifPresent { it.validate() } user() 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 = + (input.asKnown().getOrNull()?.validity() ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + (if (dimensions.asKnown().isPresent) 1 else 0) + + (encodingFormat.asKnown().getOrNull()?.validity() ?: 0) + + (if (user.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -822,15 +846,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) arrayOfStrings != null -> visitor.visitArrayOfStrings(arrayOfStrings) arrayOfTokens != null -> visitor.visitArrayOfTokens(arrayOfTokens) arrayOfTokenArrays != null -> visitor.visitArrayOfTokenArrays(arrayOfTokenArrays) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -853,6 +876,38 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitArrayOfStrings(arrayOfStrings: List) = + arrayOfStrings.size + + override fun visitArrayOfTokens(arrayOfTokens: List) = arrayOfTokens.size + + override fun visitArrayOfTokenArrays(arrayOfTokenArrays: List>) = + arrayOfTokenArrays.sumOf { it.size.toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -928,20 +983,34 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Input { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Input(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Input(arrayOfStrings = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Input(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Input(arrayOfStrings = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Input(arrayOfTokens = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>>())?.let { + Input(arrayOfTokenArrays = 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 object). + 0 -> Input(_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() } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Input(arrayOfTokens = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>>())?.let { - return Input(arrayOfTokenArrays = it, _json = json) - } - - return Input(_json = json) } } @@ -1058,6 +1127,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): EncodingFormat = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingModel.kt index 8c4c1c5ff..3c64970cd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingModel.kt @@ -98,6 +98,32 @@ class EmbeddingModel @JsonCreator private constructor(private val value: JsonFie fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): EmbeddingModel = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt index 1d39c2523..323aa78e5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileCreateParams.kt @@ -401,10 +401,18 @@ private constructor( } file() - purpose() + purpose().validate() validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleted.kt index 5f76f45b2..201cffbb7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleted.kt @@ -201,6 +201,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("file")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt index 4237a5327..ae67ba096 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.FileService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt index 84fee5853..0bde93530 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.FileServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListParams.kt index 94c591543..c3effdbc0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListParams.kt @@ -338,6 +338,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileObject.kt index e4c683be3..8aecb4a03 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileObject.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** The `File` object represents a document that has been uploaded to OpenAI. */ class FileObject @@ -444,13 +445,38 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - purpose() - status() + purpose().validate() + status().validate() expiresAt() statusDetails() 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 (id.asKnown().isPresent) 1 else 0) + + (if (bytes.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("file")) 1 else 0 } + + (purpose.asKnown().getOrNull()?.validity() ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (statusDetails.asKnown().isPresent) 1 else 0) + /** * The intended purpose of the file. Supported values are `assistants`, `assistants_output`, * `batch`, `batch_output`, `fine-tune`, `fine-tune-results` and `vision`. @@ -570,6 +596,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Purpose = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -679,6 +732,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FilePurpose.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FilePurpose.kt index 1577ab2e0..9a764b4bd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FilePurpose.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FilePurpose.kt @@ -120,6 +120,32 @@ class FilePurpose @JsonCreator private constructor(private val value: JsonField< fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): FilePurpose = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt index 9c2a0b29d..e14498304 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt @@ -20,6 +20,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -986,7 +987,7 @@ private constructor( organizationId() resultFiles() seed() - status() + status().validate() trainedTokens() trainingFile() validationFile() @@ -997,6 +998,41 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (error.asKnown().getOrNull()?.validity() ?: 0) + + (if (fineTunedModel.asKnown().isPresent) 1 else 0) + + (if (finishedAt.asKnown().isPresent) 1 else 0) + + (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("fine_tuning.job")) 1 else 0 } + + (if (organizationId.asKnown().isPresent) 1 else 0) + + (resultFiles.asKnown().getOrNull()?.size ?: 0) + + (if (seed.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (trainedTokens.asKnown().isPresent) 1 else 0) + + (if (trainingFile.asKnown().isPresent) 1 else 0) + + (if (validationFile.asKnown().isPresent) 1 else 0) + + (if (estimatedFinish.asKnown().isPresent) 1 else 0) + + (integrations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (method.asKnown().getOrNull()?.validity() ?: 0) + /** * For fine-tuning jobs that have `failed`, this will contain more information on the cause of * the failure. @@ -1202,6 +1238,26 @@ private constructor( 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 (code.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + + (if (param.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1452,6 +1508,26 @@ private constructor( 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 = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + /** * Number of examples in each batch. A larger batch size means that model parameters are * updated less frequently, but with lower variance. @@ -1479,13 +1555,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1512,6 +1587,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1567,23 +1669,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return BatchSize(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return BatchSize(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(manual = 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 object). + 0 -> BatchSize(_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() } - - return BatchSize(_json = json) } } @@ -1631,13 +1738,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) number != null -> visitor.visitNumber(number) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1664,6 +1770,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1720,23 +1853,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): LearningRateMultiplier { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return LearningRateMultiplier(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return LearningRateMultiplier(number = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { LearningRateMultiplier(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(number = 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 object). + 0 -> LearningRateMultiplier(_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() } - - return LearningRateMultiplier(_json = json) } } @@ -1785,13 +1923,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) integer != null -> visitor.visitInteger(integer) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1818,6 +1955,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1873,23 +2037,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return NEpochs(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return NEpochs(integer = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(integer = 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 object). + 0 -> NEpochs(_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() } - - return NEpochs(_json = json) } } @@ -2041,6 +2210,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2128,6 +2324,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2319,10 +2533,30 @@ private constructor( dpo().ifPresent { it.validate() } supervised().ifPresent { it.validate() } - type() + type().ifPresent { it.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 = + (dpo.asKnown().getOrNull()?.validity() ?: 0) + + (supervised.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** Configuration for the DPO fine-tuning method. */ class Dpo private constructor( @@ -2442,6 +2676,23 @@ private constructor( 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 = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + /** The hyperparameters used for the fine-tuning job. */ class Hyperparameters private constructor( @@ -2733,6 +2984,27 @@ private constructor( 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 = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (beta.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + /** * Number of examples in each batch. A larger batch size means that model parameters * are updated less frequently, but with lower variance. @@ -2760,13 +3032,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2793,6 +3064,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2848,23 +3146,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return BatchSize(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return BatchSize(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(manual = 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 object). + 0 -> BatchSize(_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() } - - return BatchSize(_json = json) } } @@ -2912,13 +3217,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2945,6 +3249,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3000,23 +3331,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Beta { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return Beta(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return Beta(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Beta(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + Beta(manual = 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 object). + 0 -> Beta(_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() } - - return Beta(_json = json) } } @@ -3064,13 +3402,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3097,6 +3434,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3160,23 +3524,32 @@ private constructor( ): LearningRateMultiplier { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return LearningRateMultiplier(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return LearningRateMultiplier(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { + LearningRateMultiplier(auto = it, _json = json) + } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(manual = 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 object). + 0 -> LearningRateMultiplier(_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() } - - return LearningRateMultiplier(_json = json) } } @@ -3226,13 +3599,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3259,6 +3631,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3314,23 +3713,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return NEpochs(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return NEpochs(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(manual = 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 object). + 0 -> NEpochs(_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() } - - return NEpochs(_json = json) } } @@ -3507,6 +3913,23 @@ private constructor( 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 = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + /** The hyperparameters used for the fine-tuning job. */ class Hyperparameters private constructor( @@ -3754,6 +4177,26 @@ private constructor( 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 = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + /** * Number of examples in each batch. A larger batch size means that model parameters * are updated less frequently, but with lower variance. @@ -3781,13 +4224,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3814,6 +4256,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3869,23 +4338,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return BatchSize(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return BatchSize(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(manual = 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 object). + 0 -> BatchSize(_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() } - - return BatchSize(_json = json) } } @@ -3933,13 +4409,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3966,6 +4441,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4029,23 +4531,32 @@ private constructor( ): LearningRateMultiplier { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return LearningRateMultiplier(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return LearningRateMultiplier(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { + LearningRateMultiplier(auto = it, _json = json) + } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(manual = 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 object). + 0 -> LearningRateMultiplier(_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() } - - return LearningRateMultiplier(_json = json) } } @@ -4095,13 +4606,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4128,6 +4638,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4183,23 +4720,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return NEpochs(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return NEpochs(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(manual = 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 object). + 0 -> NEpochs(_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() } - - return NEpochs(_json = json) } } @@ -4344,6 +4888,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEvent.kt index 251572b70..000f72dda 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEvent.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Fine-tuning job event object */ class FineTuningJobEvent @@ -312,17 +313,39 @@ private constructor( id() createdAt() - level() + level().validate() message() _object_().let { if (it != JsonValue.from("fine_tuning.job.event")) { throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - type() + type().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (level.asKnown().getOrNull()?.validity() ?: 0) + + (if (message.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("fine_tuning.job.event")) 1 else 0 } + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** The log level of the event. */ class Level @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -415,6 +438,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Level = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -514,6 +564,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegration.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegration.kt index 501ca8588..8be1d3324 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegration.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegration.kt @@ -274,6 +274,26 @@ private constructor( 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 (project.asKnown().isPresent) 1 else 0) + + (if (entity.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (tags.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObject.kt index 4daad18fc..290847bd2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObject.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class FineTuningJobWandbIntegrationObject private constructor( @@ -192,6 +193,24 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("wandb")) 1 else 0 } + + (wandb.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt index 128a217c4..05f0b8766 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -1184,6 +1185,32 @@ private constructor( 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 (model.asKnown().isPresent) 1 else 0) + + (if (trainingFile.asKnown().isPresent) 1 else 0) + + (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + + (integrations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (method.asKnown().getOrNull()?.validity() ?: 0) + + (if (seed.asKnown().isPresent) 1 else 0) + + (if (suffix.asKnown().isPresent) 1 else 0) + + (if (validationFile.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1303,6 +1330,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Model = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1549,6 +1603,26 @@ private constructor( 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 = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + /** * Number of examples in each batch. A larger batch size means that model parameters are * updated less frequently, but with lower variance. @@ -1576,13 +1650,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) integer != null -> visitor.visitInteger(integer) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1609,6 +1682,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1664,23 +1764,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return BatchSize(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return BatchSize(integer = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(integer = 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 object). + 0 -> BatchSize(_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() } - - return BatchSize(_json = json) } } @@ -1728,13 +1833,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) number != null -> visitor.visitNumber(number) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1761,6 +1865,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1817,23 +1948,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): LearningRateMultiplier { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return LearningRateMultiplier(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return LearningRateMultiplier(number = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { LearningRateMultiplier(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(number = 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 object). + 0 -> LearningRateMultiplier(_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() } - - return LearningRateMultiplier(_json = json) } } @@ -1882,13 +2018,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) integer != null -> visitor.visitInteger(integer) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1915,6 +2050,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1970,23 +2132,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return NEpochs(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return NEpochs(integer = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(integer = 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 object). + 0 -> NEpochs(_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() } - - return NEpochs(_json = json) } } @@ -2195,6 +2362,25 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("wandb")) 1 else 0 } + + (wandb.asKnown().getOrNull()?.validity() ?: 0) + /** * The settings for your integration with Weights and Biases. This payload specifies the * project that metrics will be sent to. Optionally, you can set an explicit display name @@ -2469,6 +2655,27 @@ private constructor( 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 (project.asKnown().isPresent) 1 else 0) + + (if (entity.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (tags.asKnown().getOrNull()?.size ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2579,6 +2786,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2770,10 +2995,30 @@ private constructor( dpo().ifPresent { it.validate() } supervised().ifPresent { it.validate() } - type() + type().ifPresent { it.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 = + (dpo.asKnown().getOrNull()?.validity() ?: 0) + + (supervised.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** Configuration for the DPO fine-tuning method. */ class Dpo private constructor( @@ -2893,6 +3138,23 @@ private constructor( 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 = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + /** The hyperparameters used for the fine-tuning job. */ class Hyperparameters private constructor( @@ -3184,6 +3446,27 @@ private constructor( 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 = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (beta.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + /** * Number of examples in each batch. A larger batch size means that model parameters * are updated less frequently, but with lower variance. @@ -3211,13 +3494,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3244,6 +3526,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3299,23 +3608,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return BatchSize(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return BatchSize(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(manual = 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 object). + 0 -> BatchSize(_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() } - - return BatchSize(_json = json) } } @@ -3363,13 +3679,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3396,6 +3711,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3451,23 +3793,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Beta { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return Beta(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return Beta(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Beta(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + Beta(manual = 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 object). + 0 -> Beta(_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() } - - return Beta(_json = json) } } @@ -3515,13 +3864,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3548,6 +3896,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3611,23 +3986,32 @@ private constructor( ): LearningRateMultiplier { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return LearningRateMultiplier(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return LearningRateMultiplier(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { + LearningRateMultiplier(auto = it, _json = json) + } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(manual = 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 object). + 0 -> LearningRateMultiplier(_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() } - - return LearningRateMultiplier(_json = json) } } @@ -3677,13 +4061,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -3710,6 +4093,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3765,23 +4175,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return NEpochs(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return NEpochs(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(manual = 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 object). + 0 -> NEpochs(_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() } - - return NEpochs(_json = json) } } @@ -3958,6 +4375,23 @@ private constructor( 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 = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + /** The hyperparameters used for the fine-tuning job. */ class Hyperparameters private constructor( @@ -4205,6 +4639,26 @@ private constructor( 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 = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + /** * Number of examples in each batch. A larger batch size means that model parameters * are updated less frequently, but with lower variance. @@ -4232,13 +4686,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4265,6 +4718,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4320,23 +4800,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return BatchSize(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return BatchSize(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(manual = 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 object). + 0 -> BatchSize(_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() } - - return BatchSize(_json = json) } } @@ -4384,13 +4871,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4417,6 +4903,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4480,23 +4993,32 @@ private constructor( ): LearningRateMultiplier { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return LearningRateMultiplier(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return LearningRateMultiplier(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { + LearningRateMultiplier(auto = it, _json = json) + } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(manual = 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 object). + 0 -> LearningRateMultiplier(_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() } - - return LearningRateMultiplier(_json = json) } } @@ -4546,13 +5068,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -4579,6 +5100,33 @@ private constructor( 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 visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitManual(manual: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -4634,23 +5182,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { - it.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - ?.let { - return NEpochs(auto = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef())?.let { - return NEpochs(manual = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(manual = 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 object). + 0 -> NEpochs(_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() } - - return NEpochs(_json = json) } } @@ -4795,6 +5350,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt index 3e681e8e2..4d6337125 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.finetuning.JobService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt index 4b20238f3..225ece265 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.finetuning.JobServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt index 426b43daa..c587d86b7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.finetuning.JobService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt index 5f1537131..6d2d77d28 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.finetuning.JobServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt index b143b2488..624354933 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.finetuning.jobs.CheckpointService import java.util.Collections import java.util.Objects @@ -117,6 +118,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt index 99a6a3e84..0b16be81f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.finetuning.jobs.CheckpointServiceAsync import java.util.Collections import java.util.Objects @@ -119,6 +120,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpoint.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpoint.kt index 895ea1552..5cf8032e3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpoint.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpoint.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that @@ -377,6 +378,29 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (fineTunedModelCheckpoint.asKnown().isPresent) 1 else 0) + + (if (fineTuningJobId.asKnown().isPresent) 1 else 0) + + (metrics.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("fine_tuning.job.checkpoint")) 1 else 0 } + + (if (stepNumber.asKnown().isPresent) 1 else 0) + /** Metrics at the step number during the fine-tuning job. */ class Metrics private constructor( @@ -715,6 +739,30 @@ private constructor( 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 (fullValidLoss.asKnown().isPresent) 1 else 0) + + (if (fullValidMeanTokenAccuracy.asKnown().isPresent) 1 else 0) + + (if (step.asKnown().isPresent) 1 else 0) + + (if (trainLoss.asKnown().isPresent) 1 else 0) + + (if (trainMeanTokenAccuracy.asKnown().isPresent) 1 else 0) + + (if (validLoss.asKnown().isPresent) 1 else 0) + + (if (validMeanTokenAccuracy.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/Image.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/Image.kt index b73d832ca..31ed80b75 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/Image.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/Image.kt @@ -193,6 +193,25 @@ private constructor( 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 (b64Json.asKnown().isPresent) 1 else 0) + + (if (revisedPrompt.asKnown().isPresent) 1 else 0) + + (if (url.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt index 1bc7ca27b..a75a9437b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageCreateVariationParams.kt @@ -727,14 +727,22 @@ private constructor( } image() - model() + model().ifPresent { it.validate() } n() - responseFormat() - size() + responseFormat().ifPresent { it.validate() } + size().ifPresent { it.validate() } user() validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -846,6 +854,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ResponseFormat = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -951,6 +986,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Size = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt index 7636da1cf..f78b28203 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageEditParams.kt @@ -893,14 +893,22 @@ private constructor( image() prompt() mask() - model() + model().ifPresent { it.validate() } n() - responseFormat() - size() + responseFormat().ifPresent { it.validate() } + size().ifPresent { it.validate() } user() validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1012,6 +1020,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ResponseFormat = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1117,6 +1152,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Size = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageGenerateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageGenerateParams.kt index 48150c61d..b69e0c2db 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageGenerateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageGenerateParams.kt @@ -910,16 +910,41 @@ private constructor( } prompt() - model() + model().ifPresent { it.validate() } n() - quality() - responseFormat() - size() - style() + quality().ifPresent { it.validate() } + responseFormat().ifPresent { it.validate() } + size().ifPresent { it.validate() } + style().ifPresent { it.validate() } user() 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 (prompt.asKnown().isPresent) 1 else 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + (if (n.asKnown().isPresent) 1 else 0) + + (quality.asKnown().getOrNull()?.validity() ?: 0) + + (responseFormat.asKnown().getOrNull()?.validity() ?: 0) + + (size.asKnown().getOrNull()?.validity() ?: 0) + + (style.asKnown().getOrNull()?.validity() ?: 0) + + (if (user.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1027,6 +1052,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Quality = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1133,6 +1185,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ResponseFormat = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1253,6 +1332,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Size = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1357,6 +1463,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Style = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageModel.kt index 09175fa42..b2764c2e2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/images/ImageModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/images/ImageModel.kt @@ -90,6 +90,32 @@ class ImageModel @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1879,6 +1960,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Harassment = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1981,6 +2089,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): HarassmentThreatening = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2075,6 +2210,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Hate = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2174,6 +2336,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): HateThreatening = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2271,6 +2460,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Illicit = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2370,6 +2586,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): IllicitViolent = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2473,6 +2716,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): SelfHarm = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2580,6 +2850,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): SelfHarmInstruction = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2685,6 +2982,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): SelfHarmIntent = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2787,6 +3111,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Sexual = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2885,6 +3236,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): SexualMinor = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2988,6 +3366,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Violence = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3093,6 +3498,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ViolenceGraphic = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3749,6 +4181,36 @@ private constructor( 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 (harassment.asKnown().isPresent) 1 else 0) + + (if (harassmentThreatening.asKnown().isPresent) 1 else 0) + + (if (hate.asKnown().isPresent) 1 else 0) + + (if (hateThreatening.asKnown().isPresent) 1 else 0) + + (if (illicit.asKnown().isPresent) 1 else 0) + + (if (illicitViolent.asKnown().isPresent) 1 else 0) + + (if (selfHarm.asKnown().isPresent) 1 else 0) + + (if (selfHarmInstructions.asKnown().isPresent) 1 else 0) + + (if (selfHarmIntent.asKnown().isPresent) 1 else 0) + + (if (sexual.asKnown().isPresent) 1 else 0) + + (if (sexualMinors.asKnown().isPresent) 1 else 0) + + (if (violence.asKnown().isPresent) 1 else 0) + + (if (violenceGraphic.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateParams.kt index 9914e3d38..d44786e5f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateParams.kt @@ -20,6 +20,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -28,6 +29,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Classifies if text and/or image inputs are potentially harmful. Learn more in the @@ -488,10 +490,29 @@ private constructor( } input().validate() - model() + model().ifPresent { it.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 = + (input.asKnown().getOrNull()?.validity() ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -552,15 +573,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) strings != null -> visitor.visitStrings(strings) moderationMultiModalArray != null -> visitor.visitModerationMultiModalArray(moderationMultiModalArray) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -585,6 +605,36 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitStrings(strings: List) = strings.size + + override fun visitModerationMultiModalArray( + moderationMultiModalArray: List + ) = moderationMultiModalArray.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -654,20 +704,30 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Input { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Input(string = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Input(strings = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Input(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Input(strings = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Input(moderationMultiModalArray = 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 object). + 0 -> Input(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Input(moderationMultiModalArray = it, _json = json) - } - - return Input(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateResponse.kt index 24bd3f2c8..1804341cf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationCreateResponse.kt @@ -16,6 +16,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Represents if a given text input is potentially harmful. */ class ModerationCreateResponse @@ -226,6 +227,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (results.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationImageUrlInput.kt b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationImageUrlInput.kt index 2d8338389..f91541725 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationImageUrlInput.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationImageUrlInput.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** An object describing an image to classify. */ class ModerationImageUrlInput @@ -177,6 +178,24 @@ private constructor( 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 = + (imageUrl.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("image_url")) 1 else 0 } + /** Contains either an image URL or a data URL for a base64 encoded image. */ class ImageUrl private constructor( @@ -299,6 +318,22 @@ private constructor( 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 (url.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationModel.kt index fddd0cf52..d541e3e2b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationModel.kt @@ -105,6 +105,32 @@ class ModerationModel @JsonCreator private constructor(private val value: JsonFi fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ModerationModel = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationMultiModalInput.kt b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationMultiModalInput.kt index 15c9d256a..f42d93f53 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationMultiModalInput.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationMultiModalInput.kt @@ -46,13 +46,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { imageUrl != null -> visitor.visitImageUrl(imageUrl) text != null -> visitor.visitText(text) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -75,6 +74,31 @@ private constructor( 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 visitImageUrl(imageUrl: ModerationImageUrlInput) = imageUrl.validity() + + override fun visitText(text: ModerationTextInput) = text.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -140,16 +164,14 @@ private constructor( when (type) { "image_url" -> { - return ModerationMultiModalInput( - imageUrl = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ModerationMultiModalInput(imageUrl = it, _json = json) + } ?: ModerationMultiModalInput(_json = json) } "text" -> { - return ModerationMultiModalInput( - text = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ModerationMultiModalInput(text = it, _json = json) + } ?: ModerationMultiModalInput(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationTextInput.kt b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationTextInput.kt index 9caab0c18..5eb85fe1f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationTextInput.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/moderations/ModerationTextInput.kt @@ -176,6 +176,24 @@ private constructor( 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("text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt index 9bd52e667..486080c43 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ComputerTool.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * A tool that controls a virtual computer. Learn more about the @@ -260,7 +261,7 @@ private constructor( displayHeight() displayWidth() - environment() + environment().validate() _type().let { if (it != JsonValue.from("computer_use_preview")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -269,6 +270,26 @@ private constructor( 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 (displayHeight.asKnown().isPresent) 1 else 0) + + (if (displayWidth.asKnown().isPresent) 1 else 0) + + (environment.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("computer_use_preview")) 1 else 0 } + /** The type of computer environment to control. */ class Environment @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -370,6 +391,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Environment = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/EasyInputMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/EasyInputMessage.kt index a2288f396..603ab1c21 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/EasyInputMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/EasyInputMessage.kt @@ -20,12 +20,14 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A message input to the model with a role indicating instruction following hierarchy. Instructions @@ -232,11 +234,30 @@ private constructor( } content().validate() - role() - type() + role().validate() + type().ifPresent { it.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 = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** * Text, image, or audio input to the model, used to generate a response. Can also contain * previous assistant responses. @@ -270,14 +291,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { textInput != null -> visitor.visitTextInput(textInput) responseInputMessageContentList != null -> visitor.visitResponseInputMessageContentList(responseInputMessageContentList) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -300,6 +320,34 @@ private constructor( 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 visitResponseInputMessageContentList( + responseInputMessageContentList: List + ) = responseInputMessageContentList.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -368,17 +416,29 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Content { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Content(textInput = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(textInput = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { + Content(responseInputMessageContentList = 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 object). + 0 -> Content(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Content(responseInputMessageContentList = it, _json = json) - } - - return Content(_json = json) } } @@ -498,6 +558,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -591,6 +678,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt index 5b1f74758..371530bd8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/FileSearchTool.kt @@ -20,6 +20,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -30,6 +31,7 @@ import com.openai.models.CompoundFilter import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A tool that searches for relevant content from uploaded files. Learn more about the @@ -339,6 +341,27 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("file_search")) 1 else 0 } + + (vectorStoreIds.asKnown().getOrNull()?.size ?: 0) + + (filters.asKnown().getOrNull()?.validity() ?: 0) + + (if (maxNumResults.asKnown().isPresent) 1 else 0) + + (rankingOptions.asKnown().getOrNull()?.validity() ?: 0) + /** A filter to apply based on file attributes. */ @JsonDeserialize(using = Filters.Deserializer::class) @JsonSerialize(using = Filters.Serializer::class) @@ -373,13 +396,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { comparisonFilter != null -> visitor.visitComparisonFilter(comparisonFilter) compoundFilter != null -> visitor.visitCompoundFilter(compoundFilter) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -402,6 +424,34 @@ private constructor( 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 visitComparisonFilter(comparisonFilter: ComparisonFilter) = + comparisonFilter.validity() + + override fun visitCompoundFilter(compoundFilter: CompoundFilter) = + compoundFilter.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -470,16 +520,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Filters { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Filters(comparisonFilter = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Filters(compoundFilter = it, _json = json) - } - - return Filters(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Filters(comparisonFilter = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Filters(compoundFilter = 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 boolean). + 0 -> Filters(_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() + } } } @@ -649,11 +711,30 @@ private constructor( return@apply } - ranker() + ranker().ifPresent { it.validate() } scoreThreshold() 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 = + (ranker.asKnown().getOrNull()?.validity() ?: 0) + + (if (scoreThreshold.asKnown().isPresent) 1 else 0) + /** The ranker to use for the file search. */ class Ranker @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -744,6 +825,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Ranker = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt index 6518df95e..24a57b352 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/FunctionTool.kt @@ -298,6 +298,27 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + + (parameters.asKnown().getOrNull()?.validity() ?: 0) + + (if (strict.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + + (if (description.asKnown().isPresent) 1 else 0) + /** A JSON schema object describing the parameters of the function. */ class Parameters @JsonCreator @@ -365,6 +386,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt index 4ab9c3ba2..c717e09c3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt @@ -20,6 +20,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -1245,14 +1246,52 @@ private constructor( maxOutputTokens() previousResponseId() reasoning().ifPresent { it.validate() } - status() + status().ifPresent { it.validate() } text().ifPresent { it.validate() } - truncation() + truncation().ifPresent { it.validate() } usage().ifPresent { it.validate() } user() 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (error.asKnown().getOrNull()?.validity() ?: 0) + + (incompleteDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("response")) 1 else 0 } + + (output.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (parallelToolCalls.asKnown().isPresent) 1 else 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (toolChoice.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + + (if (maxOutputTokens.asKnown().isPresent) 1 else 0) + + (if (previousResponseId.asKnown().isPresent) 1 else 0) + + (reasoning.asKnown().getOrNull()?.validity() ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (text.asKnown().getOrNull()?.validity() ?: 0) + + (truncation.asKnown().getOrNull()?.validity() ?: 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + + (if (user.asKnown().isPresent) 1 else 0) + /** Details about why the response is incomplete. */ class IncompleteDetails private constructor( @@ -1357,10 +1396,26 @@ private constructor( return@apply } - reason() + reason().ifPresent { it.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 = (reason.asKnown().getOrNull()?.validity() ?: 0) + /** The reason why the response is incomplete. */ class Reason @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1451,6 +1506,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Reason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1556,6 +1638,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1637,14 +1737,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { options != null -> visitor.visitOptions(options) types != null -> visitor.visitTypes(types) function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -1655,7 +1754,9 @@ private constructor( accept( object : Visitor { - override fun visitOptions(options: ToolChoiceOptions) {} + override fun visitOptions(options: ToolChoiceOptions) { + options.validate() + } override fun visitTypes(types: ToolChoiceTypes) { types.validate() @@ -1669,6 +1770,34 @@ private constructor( 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 visitOptions(options: ToolChoiceOptions) = options.validity() + + override fun visitTypes(types: ToolChoiceTypes) = types.validity() + + override fun visitFunction(function: ToolChoiceFunction) = function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1759,19 +1888,31 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): ToolChoice { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return ToolChoice(options = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ToolChoice(options = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ToolChoice(types = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ToolChoice(function = 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 -> ToolChoice(_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() } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ToolChoice(types = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ToolChoice(function = it, _json = json) - } - - return ToolChoice(_json = json) } } @@ -1888,6 +2029,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Truncation = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDeltaEvent.kt index 31b7e4037..9277883e0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDeltaEvent.kt @@ -176,6 +176,24 @@ private constructor( 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 (delta.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.audio.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDoneEvent.kt index 1eba36a04..a0a92d606 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioDoneEvent.kt @@ -125,6 +125,23 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("response.audio.done")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEvent.kt index da95d1a6b..99297177c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEvent.kt @@ -179,6 +179,24 @@ private constructor( 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 (delta.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.audio.transcript.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEvent.kt index ded80098f..b673211a9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEvent.kt @@ -130,6 +130,23 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("response.audio.transcript.done")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEvent.kt index 1aa63012f..15f210a22 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEvent.kt @@ -217,6 +217,27 @@ private constructor( 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 (delta.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("response.code_interpreter_call.code.delta")) 1 else 0 + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEvent.kt index 4a378dec7..bbf8344ec 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEvent.kt @@ -217,6 +217,27 @@ private constructor( 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 (code.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("response.code_interpreter_call.code.done")) 1 else 0 + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEvent.kt index 5f7ea869c..5ead1cc04 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when the code interpreter call is completed. */ class ResponseCodeInterpreterCallCompletedEvent @@ -228,6 +229,27 @@ private constructor( 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 = + (codeInterpreterCall.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("response.code_interpreter_call.completed")) 1 else 0 + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEvent.kt index 82549ac0a..44a080c0c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when a code interpreter call is in progress. */ class ResponseCodeInterpreterCallInProgressEvent @@ -228,6 +229,27 @@ private constructor( 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 = + (codeInterpreterCall.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("response.code_interpreter_call.in_progress")) 1 else 0 + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEvent.kt index d2f353b2f..35487938d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when the code interpreter is actively interpreting the code snippet. */ class ResponseCodeInterpreterCallInterpretingEvent @@ -229,6 +230,27 @@ private constructor( 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 = + (codeInterpreterCall.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("response.code_interpreter_call.interpreting")) 1 else 0 + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCall.kt index fa65307ed..49c56491a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCall.kt @@ -331,7 +331,7 @@ private constructor( id() code() results().forEach { it.validate() } - status() + status().validate() _type().let { if (it != JsonValue.from("code_interpreter_call")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -340,6 +340,27 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (code.asKnown().isPresent) 1 else 0) + + (results.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("code_interpreter_call")) 1 else 0 } + /** The output of a code interpreter tool call that is text. */ @JsonDeserialize(using = Result.Deserializer::class) @JsonSerialize(using = Result.Serializer::class) @@ -368,13 +389,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { logs != null -> visitor.visitLogs(logs) files != null -> visitor.visitFiles(files) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -397,6 +417,32 @@ private constructor( 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 visitLogs(logs: Logs) = logs.validity() + + override fun visitFiles(files: Files) = files.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -456,16 +502,14 @@ private constructor( when (type) { "logs" -> { - return Result( - logs = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Result(logs = it, _json = json) + } ?: Result(_json = json) } "files" -> { - return Result( - files = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Result(files = it, _json = json) + } ?: Result(_json = json) } } @@ -651,6 +695,25 @@ private constructor( 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 (logs.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -848,6 +911,25 @@ private constructor( 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 = + (files.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + type.let { if (it == JsonValue.from("files")) 1 else 0 } + class File private constructor( private val fileId: JsonField, @@ -1020,6 +1102,25 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (if (mimeType.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1149,6 +1250,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCompletedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCompletedEvent.kt index a42df84ea..be47d3c95 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCompletedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCompletedEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when the model response is complete. */ class ResponseCompletedEvent @@ -177,6 +178,24 @@ private constructor( 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 = + (response.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCall.kt index e11aacb6a..d2f88d491 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCall.kt @@ -409,11 +409,33 @@ private constructor( action().validate() callId() pendingSafetyChecks().forEach { it.validate() } - status() - type() + status().validate() + type().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 = + (if (id.asKnown().isPresent) 1 else 0) + + (action.asKnown().getOrNull()?.validity() ?: 0) + + (if (callId.asKnown().isPresent) 1 else 0) + + (pendingSafetyChecks.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** A click action. */ @JsonDeserialize(using = Action.Deserializer::class) @JsonSerialize(using = Action.Serializer::class) @@ -505,8 +527,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { click != null -> visitor.visitClick(click) doubleClick != null -> visitor.visitDoubleClick(doubleClick) drag != null -> visitor.visitDrag(drag) @@ -518,7 +540,6 @@ private constructor( wait != null -> visitor.visitWait(wait) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -579,6 +600,50 @@ private constructor( 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 visitClick(click: Click) = click.validity() + + override fun visitDoubleClick(doubleClick: DoubleClick) = doubleClick.validity() + + override fun visitDrag(drag: Drag) = drag.validity() + + override fun visitKeypress(keypress: Keypress) = keypress.validity() + + override fun visitMove(move: Move) = move.validity() + + override fun visitScreenshot(screenshot: JsonValue) = + screenshot.let { + if (it == JsonValue.from(mapOf("type" to "screenshot"))) 1 else 0 + } + + override fun visitScroll(scroll: Scroll) = scroll.validity() + + override fun visitType(type: Type) = type.validity() + + override fun visitWait(wait: JsonValue) = + wait.let { if (it == JsonValue.from(mapOf("type" to "wait"))) 1 else 0 } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -689,58 +754,49 @@ private constructor( when (type) { "click" -> { - return Action( - click = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(click = it, _json = json) + } ?: Action(_json = json) } "double_click" -> { - return Action( - doubleClick = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(doubleClick = it, _json = json) + } ?: Action(_json = json) } "drag" -> { - return Action( - drag = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(drag = it, _json = json) + } ?: Action(_json = json) } "keypress" -> { - return Action( - keypress = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(keypress = it, _json = json) + } ?: Action(_json = json) } "move" -> { - return Action( - move = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(move = it, _json = json) + } ?: Action(_json = json) } "screenshot" -> { - return Action( - screenshot = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Action(screenshot = it, _json = json) } + ?.takeIf { it.isValid() } ?: Action(_json = json) } "scroll" -> { - return Action( - scroll = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(scroll = it, _json = json) + } ?: Action(_json = json) } "type" -> { - return Action( - type = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Action(type = it, _json = json) + } ?: Action(_json = json) } "wait" -> { - return Action( - wait = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { Action(wait = it, _json = json) } + ?.takeIf { it.isValid() } ?: Action(_json = json) } } @@ -1004,7 +1060,7 @@ private constructor( return@apply } - button() + button().validate() _type().let { if (it != JsonValue.from("click")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -1015,6 +1071,27 @@ private constructor( 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 = + (button.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("click")) 1 else 0 } + + (if (x.asKnown().isPresent) 1 else 0) + + (if (y.asKnown().isPresent) 1 else 0) + /** * Indicates which mouse button was pressed during the click. One of `left`, `right`, * `wheel`, `back`, or `forward`. @@ -1128,6 +1205,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Button = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1362,6 +1466,26 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("double_click")) 1 else 0 } + + (if (x.asKnown().isPresent) 1 else 0) + + (if (y.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1580,6 +1704,25 @@ private constructor( 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 = + (path.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + type.let { if (it == JsonValue.from("drag")) 1 else 0 } + /** A series of x/y coordinate pairs in the drag path. */ class Path private constructor( @@ -1745,6 +1888,24 @@ private constructor( 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 (x.asKnown().isPresent) 1 else 0) + (if (y.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1968,6 +2129,25 @@ private constructor( 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 = + (keys.asKnown().getOrNull()?.size ?: 0) + + type.let { if (it == JsonValue.from("keypress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2188,6 +2368,26 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("move")) 1 else 0 } + + (if (x.asKnown().isPresent) 1 else 0) + + (if (y.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2485,6 +2685,28 @@ private constructor( 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 (scrollX.asKnown().isPresent) 1 else 0) + + (if (scrollY.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("scroll")) 1 else 0 } + + (if (x.asKnown().isPresent) 1 else 0) + + (if (y.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2665,6 +2887,25 @@ private constructor( 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("type")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2879,6 +3120,26 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (code.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2992,6 +3253,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -3085,6 +3373,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItem.kt index 803e81d70..670b0b3c1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItem.kt @@ -18,6 +18,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ResponseComputerToolCallOutputItem private constructor( @@ -350,10 +351,32 @@ private constructor( } } acknowledgedSafetyChecks().ifPresent { it.forEach { it.validate() } } - status() + status().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (if (callId.asKnown().isPresent) 1 else 0) + + (output.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("computer_call_output")) 1 else 0 } + + (acknowledgedSafetyChecks.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + /** A pending safety check for the computer call. */ class AcknowledgedSafetyCheck private constructor( @@ -549,6 +572,26 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (code.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -662,6 +705,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshot.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshot.kt index 55bd53858..4ba009922 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshot.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshot.kt @@ -201,6 +201,25 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("computer_screenshot")) 1 else 0 } + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (imageUrl.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContent.kt index 9e88b3627..d63a9fffe 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContent.kt @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.BaseDeserializer import com.openai.core.BaseSerializer import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -78,8 +79,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { inputText != null -> visitor.visitInputText(inputText) inputImage != null -> visitor.visitInputImage(inputImage) inputFile != null -> visitor.visitInputFile(inputFile) @@ -87,7 +88,6 @@ private constructor( outputRefusal != null -> visitor.visitOutputRefusal(outputRefusal) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -122,6 +122,38 @@ private constructor( 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 visitInputText(inputText: ResponseInputText) = inputText.validity() + + override fun visitInputImage(inputImage: ResponseInputImage) = inputImage.validity() + + override fun visitInputFile(inputFile: ResponseInputFile) = inputFile.validity() + + override fun visitOutputText(outputText: ResponseOutputText) = outputText.validity() + + override fun visitOutputRefusal(outputRefusal: ResponseOutputRefusal) = + outputRefusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -214,28 +246,36 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): ResponseContent { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseContent(inputText = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseContent(inputImage = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseContent(inputFile = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseContent(outputText = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseContent(outputRefusal = it, _json = json) - } - - return ResponseContent(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseContent(inputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseContent(inputImage = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseContent(inputFile = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseContent(outputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseContent(outputRefusal = 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 boolean). + 0 -> ResponseContent(_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() + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartAddedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartAddedEvent.kt index 06d062142..89be789e9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartAddedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartAddedEvent.kt @@ -316,6 +316,27 @@ private constructor( 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 (contentIndex.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + (part.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.content_part.added")) 1 else 0 } + /** The content part that was added. */ @JsonDeserialize(using = Part.Deserializer::class) @JsonSerialize(using = Part.Serializer::class) @@ -344,13 +365,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { outputText != null -> visitor.visitOutputText(outputText) refusal != null -> visitor.visitRefusal(refusal) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -373,6 +393,33 @@ private constructor( 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 visitOutputText(outputText: ResponseOutputText) = + outputText.validity() + + override fun visitRefusal(refusal: ResponseOutputRefusal) = refusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -432,16 +479,14 @@ private constructor( when (type) { "output_text" -> { - return Part( - outputText = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Part(outputText = it, _json = json) + } ?: Part(_json = json) } "refusal" -> { - return Part( - refusal = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Part(refusal = it, _json = json) + } ?: Part(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartDoneEvent.kt index 09a36f853..4c4fe23ba 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseContentPartDoneEvent.kt @@ -315,6 +315,27 @@ private constructor( 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 (contentIndex.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + (part.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.content_part.done")) 1 else 0 } + /** The content part that is done. */ @JsonDeserialize(using = Part.Deserializer::class) @JsonSerialize(using = Part.Serializer::class) @@ -343,13 +364,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { outputText != null -> visitor.visitOutputText(outputText) refusal != null -> visitor.visitRefusal(refusal) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -372,6 +392,33 @@ private constructor( 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 visitOutputText(outputText: ResponseOutputText) = + outputText.validity() + + override fun visitRefusal(refusal: ResponseOutputRefusal) = refusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -431,16 +478,14 @@ private constructor( when (type) { "output_text" -> { - return Part( - outputText = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Part(outputText = it, _json = json) + } ?: Part(_json = json) } "refusal" -> { - return Part( - refusal = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Part(refusal = it, _json = json) + } ?: Part(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt index 0011357ed..00823cd40 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -2023,7 +2024,7 @@ private constructor( input().validate() model().validate() - include() + include().ifPresent { it.forEach { it.validate() } } instructions() maxOutputTokens() metadata().ifPresent { it.validate() } @@ -2036,11 +2037,45 @@ private constructor( toolChoice().ifPresent { it.validate() } tools().ifPresent { it.forEach { it.validate() } } topP() - truncation() + truncation().ifPresent { it.validate() } user() 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 = + (input.asKnown().getOrNull()?.validity() ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + + (include.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (instructions.asKnown().isPresent) 1 else 0) + + (if (maxOutputTokens.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (parallelToolCalls.asKnown().isPresent) 1 else 0) + + (if (previousResponseId.asKnown().isPresent) 1 else 0) + + (reasoning.asKnown().getOrNull()?.validity() ?: 0) + + (if (store.asKnown().isPresent) 1 else 0) + + (if (temperature.asKnown().isPresent) 1 else 0) + + (text.asKnown().getOrNull()?.validity() ?: 0) + + (toolChoice.asKnown().getOrNull()?.validity() ?: 0) + + (tools.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (topP.asKnown().isPresent) 1 else 0) + + (truncation.asKnown().getOrNull()?.validity() ?: 0) + + (if (user.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2096,13 +2131,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) response != null -> visitor.visitResponse(response) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2123,6 +2157,33 @@ private constructor( 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 visitText(text: String) = 1 + + override fun visitResponse(response: List) = + response.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2184,17 +2245,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Input { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Input(text = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Input(text = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Input(response = 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 object). + 0 -> Input(_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() } - tryDeserialize(node, jacksonTypeRef>()) { - it.forEach { it.validate() } - } - ?.let { - return Input(response = it, _json = json) - } - - return Input(_json = json) } } @@ -2289,6 +2361,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2370,14 +2460,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { options != null -> visitor.visitOptions(options) types != null -> visitor.visitTypes(types) function != null -> visitor.visitFunction(function) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -2388,7 +2477,9 @@ private constructor( accept( object : Visitor { - override fun visitOptions(options: ToolChoiceOptions) {} + override fun visitOptions(options: ToolChoiceOptions) { + options.validate() + } override fun visitTypes(types: ToolChoiceTypes) { types.validate() @@ -2402,6 +2493,34 @@ private constructor( 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 visitOptions(options: ToolChoiceOptions) = options.validity() + + override fun visitTypes(types: ToolChoiceTypes) = types.validity() + + override fun visitFunction(function: ToolChoiceFunction) = function.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2492,19 +2611,31 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): ToolChoice { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return ToolChoice(options = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ToolChoice(options = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ToolChoice(types = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ToolChoice(function = 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 -> ToolChoice(_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() } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ToolChoice(types = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ToolChoice(function = it, _json = json) - } - - return ToolChoice(_json = json) } } @@ -2621,6 +2752,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Truncation = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreatedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreatedEvent.kt index b691bc280..d315aff3d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreatedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreatedEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** An event that is emitted when a response is created. */ class ResponseCreatedEvent @@ -177,6 +178,24 @@ private constructor( 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 = + (response.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.created")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseError.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseError.kt index 0b4fd74e2..bd9856876 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseError.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseError.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** An error object returned when the model fails to generate a Response. */ class ResponseError @@ -169,11 +170,28 @@ private constructor( return@apply } - code() + code().validate() message() 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 = + (code.asKnown().getOrNull()?.validity() ?: 0) + (if (message.asKnown().isPresent) 1 else 0) + /** The error code for the response. */ class Code @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -356,6 +374,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Code = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseErrorEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseErrorEvent.kt index 5480ba6a5..057b14ed4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseErrorEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseErrorEvent.kt @@ -252,6 +252,26 @@ private constructor( 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 (code.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + + (if (param.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("error")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFailedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFailedEvent.kt index da809bfac..f87fdd27b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFailedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFailedEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** An event that is emitted when a response fails. */ class ResponseFailedEvent @@ -177,6 +178,24 @@ private constructor( 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 = + (response.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.failed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEvent.kt index aa8c704c4..4f0b949a6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEvent.kt @@ -217,6 +217,25 @@ private constructor( 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 (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.file_search_call.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEvent.kt index ebaa0e7cb..6c1a63794 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEvent.kt @@ -217,6 +217,25 @@ private constructor( 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 (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.file_search_call.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEvent.kt index 2103fa45a..7a69c46ea 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEvent.kt @@ -217,6 +217,25 @@ private constructor( 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 (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.file_search_call.searching")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt index e5fcb9e07..845981fd8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt @@ -314,7 +314,7 @@ private constructor( id() queries() - status() + status().validate() _type().let { if (it != JsonValue.from("file_search_call")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -324,6 +324,27 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (queries.asKnown().getOrNull()?.size ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("file_search_call")) 1 else 0 } + + (results.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + /** * The status of the file search tool call. One of `in_progress`, `searching`, `incomplete` or * `failed`, @@ -431,6 +452,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -708,6 +756,28 @@ private constructor( 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 = + (attributes.asKnown().getOrNull()?.validity() ?: 0) + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + (if (score.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for * storing additional information about the object in a structured format, and querying for @@ -784,6 +854,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextConfig.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextConfig.kt index 2b01f4be6..7b9fc7fbd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextConfig.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextConfig.kt @@ -84,14 +84,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { text != null -> visitor.visitText(text) jsonSchema != null -> visitor.visitJsonSchema(jsonSchema) jsonObject != null -> visitor.visitJsonObject(jsonObject) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -118,6 +117,35 @@ private constructor( 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 visitText(text: ResponseFormatText) = text.validity() + + override fun visitJsonSchema(jsonSchema: ResponseFormatTextJsonSchemaConfig) = + jsonSchema.validity() + + override fun visitJsonObject(jsonObject: ResponseFormatJsonObject) = + jsonObject.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -206,23 +234,22 @@ private constructor( when (type) { "text" -> { - return ResponseFormatTextConfig( - text = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFormatTextConfig(text = it, _json = json) + } ?: ResponseFormatTextConfig(_json = json) } "json_schema" -> { - return ResponseFormatTextConfig( - jsonSchema = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseFormatTextConfig(jsonSchema = it, _json = json) } + ?: ResponseFormatTextConfig(_json = json) } "json_object" -> { - return ResponseFormatTextConfig( - jsonObject = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFormatTextConfig(jsonObject = it, _json = json) + } ?: ResponseFormatTextConfig(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfig.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfig.kt index 6a35c3744..6e2023eae 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfig.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfig.kt @@ -317,6 +317,27 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + + (schema.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("json_schema")) 1 else 0 } + + (if (description.asKnown().isPresent) 1 else 0) + + (if (strict.asKnown().isPresent) 1 else 0) + /** * The schema for the response format, described as a JSON Schema object. Learn how to build * JSON schemas [here](https://json-schema.org/). @@ -387,6 +408,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEvent.kt index 5a19f37c9..6f001d38d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEvent.kt @@ -251,6 +251,28 @@ private constructor( 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 (delta.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { + if (it == JsonValue.from("response.function_call_arguments.delta")) 1 else 0 + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt index 458fcf681..859ea0277 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt @@ -250,6 +250,26 @@ private constructor( 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 (arguments.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.function_call_arguments.done")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCall.kt index 7f539ca79..75a580699 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCall.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A tool call to run a function. See the @@ -315,10 +316,32 @@ private constructor( } } id() - status() + status().ifPresent { it.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 = + (if (arguments.asKnown().isPresent) 1 else 0) + + (if (callId.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function_call")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when * items are returned via API. @@ -414,6 +437,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallItem.kt index 7f09c7024..40fe2d875 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallItem.kt @@ -15,6 +15,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A tool call to run a function. See the @@ -332,10 +333,32 @@ private constructor( } } id() - status() + status().ifPresent { it.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 = + (if (arguments.asKnown().isPresent) 1 else 0) + + (if (callId.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function_call")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt index d1087e376..45f7b229f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt @@ -16,6 +16,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ResponseFunctionToolCallOutputItem private constructor( @@ -280,10 +281,31 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - status() + status().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (if (callId.asKnown().isPresent) 1 else 0) + + (if (output.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function_call_output")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when * items are returned via API. @@ -379,6 +401,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt index 3c5559d83..095336fc0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionWebSearch.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * The results of a web search tool call. See the @@ -206,7 +207,7 @@ private constructor( } id() - status() + status().validate() _type().let { if (it != JsonValue.from("web_search_call")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -215,6 +216,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("web_search_call")) 1 else 0 } + /** The status of the web search tool call. */ class Status @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -313,6 +333,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInProgressEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInProgressEvent.kt index 5ace9897a..1a46300ea 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInProgressEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInProgressEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when the response is in progress. */ class ResponseInProgressEvent @@ -177,6 +178,24 @@ private constructor( 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 = + (response.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt index f16f7257c..b85d679af 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncludable.kt @@ -107,6 +107,32 @@ class ResponseIncludable @JsonCreator private constructor(private val value: Jso fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ResponseIncludable = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncompleteEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncompleteEvent.kt index 6e005be2f..098b2a1df 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncompleteEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseIncompleteEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** An event that is emitted when a response finishes as incomplete. */ class ResponseIncompleteEvent @@ -177,6 +178,24 @@ private constructor( 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 = + (response.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("response.incomplete")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputAudio.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputAudio.kt index 0c11ddc98..a2c824a7e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputAudio.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputAudio.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** An audio input to the model. */ class ResponseInputAudio @@ -202,7 +203,7 @@ private constructor( } data() - format() + format().validate() _type().let { if (it != JsonValue.from("input_audio")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -211,6 +212,25 @@ private constructor( 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 (data.asKnown().isPresent) 1 else 0) + + (format.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("input_audio")) 1 else 0 } + /** The format of the audio data. Currently supported formats are `mp3` and `wav`. */ class Format @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -297,6 +317,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Format = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputContent.kt index 3c7bf5b1c..a09c60d86 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputContent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputContent.kt @@ -61,14 +61,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { inputText != null -> visitor.visitInputText(inputText) inputImage != null -> visitor.visitInputImage(inputImage) inputFile != null -> visitor.visitInputFile(inputFile) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -95,6 +94,33 @@ private constructor( 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 visitInputText(inputText: ResponseInputText) = inputText.validity() + + override fun visitInputImage(inputImage: ResponseInputImage) = inputImage.validity() + + override fun visitInputFile(inputFile: ResponseInputFile) = inputFile.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -175,22 +201,19 @@ private constructor( when (type) { "input_text" -> { - return ResponseInputContent( - inputText = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputContent(inputText = it, _json = json) + } ?: ResponseInputContent(_json = json) } "input_image" -> { - return ResponseInputContent( - inputImage = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputContent(inputImage = it, _json = json) + } ?: ResponseInputContent(_json = json) } "input_file" -> { - return ResponseInputContent( - inputFile = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputContent(inputFile = it, _json = json) + } ?: ResponseInputContent(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt index 495a1cb67..8b90f034e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFile.kt @@ -220,6 +220,26 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("input_file")) 1 else 0 } + + (if (fileData.asKnown().isPresent) 1 else 0) + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt index 11999b52e..e3c587194 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImage.kt @@ -249,7 +249,7 @@ private constructor( return@apply } - detail() + detail().validate() _type().let { if (it != JsonValue.from("input_image")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -260,6 +260,26 @@ private constructor( 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 = + (detail.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("input_image")) 1 else 0 } + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (imageUrl.asKnown().isPresent) 1 else 0) + /** * The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. * Defaults to `auto`. @@ -355,6 +375,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Detail = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt index 6c846bd18..2605583e9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt @@ -20,6 +20,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow @@ -202,8 +203,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { easyInputMessage != null -> visitor.visitEasyInputMessage(easyInputMessage) message != null -> visitor.visitMessage(message) responseOutputMessage != null -> @@ -218,7 +219,6 @@ private constructor( itemReference != null -> visitor.visitItemReference(itemReference) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -279,6 +279,59 @@ private constructor( 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 visitEasyInputMessage(easyInputMessage: EasyInputMessage) = + easyInputMessage.validity() + + override fun visitMessage(message: Message) = message.validity() + + override fun visitResponseOutputMessage( + responseOutputMessage: ResponseOutputMessage + ) = responseOutputMessage.validity() + + override fun visitFileSearchCall(fileSearchCall: ResponseFileSearchToolCall) = + fileSearchCall.validity() + + override fun visitComputerCall(computerCall: ResponseComputerToolCall) = + computerCall.validity() + + override fun visitComputerCallOutput(computerCallOutput: ComputerCallOutput) = + computerCallOutput.validity() + + override fun visitWebSearchCall(webSearchCall: ResponseFunctionWebSearch) = + webSearchCall.validity() + + override fun visitFunctionCall(functionCall: ResponseFunctionToolCall) = + functionCall.validity() + + override fun visitFunctionCallOutput(functionCallOutput: FunctionCallOutput) = + functionCallOutput.validity() + + override fun visitReasoning(reasoning: ResponseReasoningItem) = reasoning.validity() + + override fun visitItemReference(itemReference: ItemReference) = + itemReference.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -482,72 +535,71 @@ private constructor( when (type) { "message" -> { - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseInputItem(easyInputMessage = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseInputItem(message = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseInputItem(responseOutputMessage = it, _json = json) - } + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(easyInputMessage = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(message = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(responseOutputMessage = 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 boolean). + 0 -> ResponseInputItem(_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() + } } "file_search_call" -> { - return ResponseInputItem( - fileSearchCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(fileSearchCall = it, _json = json) + } ?: ResponseInputItem(_json = json) } "computer_call" -> { - return ResponseInputItem( - computerCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(computerCall = it, _json = json) + } ?: ResponseInputItem(_json = json) } "computer_call_output" -> { - return ResponseInputItem( - computerCallOutput = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(computerCallOutput = it, _json = json) + } ?: ResponseInputItem(_json = json) } "web_search_call" -> { - return ResponseInputItem( - webSearchCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(webSearchCall = it, _json = json) + } ?: ResponseInputItem(_json = json) } "function_call" -> { - return ResponseInputItem( - functionCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(functionCall = it, _json = json) + } ?: ResponseInputItem(_json = json) } "function_call_output" -> { - return ResponseInputItem( - functionCallOutput = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(functionCallOutput = it, _json = json) + } ?: ResponseInputItem(_json = json) } "reasoning" -> { - return ResponseInputItem( - reasoning = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(reasoning = it, _json = json) + } ?: ResponseInputItem(_json = json) } "item_reference" -> { - return ResponseInputItem( - itemReference = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseInputItem(itemReference = it, _json = json) + } ?: ResponseInputItem(_json = json) } } @@ -869,12 +921,33 @@ private constructor( } content().forEach { it.validate() } - role() - status() - type() + role().validate() + status().ifPresent { it.validate() } + type().ifPresent { it.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 = + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** The role of the message input. One of `user`, `system`, or `developer`. */ class Role @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -969,6 +1042,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1081,6 +1181,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1176,6 +1303,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1539,10 +1693,34 @@ private constructor( } id() acknowledgedSafetyChecks().ifPresent { it.forEach { it.validate() } } - status() + status().ifPresent { it.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 = + (if (callId.asKnown().isPresent) 1 else 0) + + (output.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("computer_call_output")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (acknowledgedSafetyChecks.asKnown().getOrNull()?.sumOf { it.validity().toInt() } + ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + /** A pending safety check for the computer call. */ class AcknowledgedSafetyCheck private constructor( @@ -1748,6 +1926,26 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (code.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1865,6 +2063,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2163,10 +2388,32 @@ private constructor( } } id() - status() + status().ifPresent { it.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 = + (if (callId.asKnown().isPresent) 1 else 0) + + (if (output.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function_call_output")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated * when items are returned via API. @@ -2266,6 +2513,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2455,6 +2729,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("item_reference")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputMessageItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputMessageItem.kt index a1b02fe0b..10c97b9b0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputMessageItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputMessageItem.kt @@ -18,6 +18,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull class ResponseInputMessageItem private constructor( @@ -325,12 +326,33 @@ private constructor( id() content().forEach { it.validate() } - role() - status() - type() + role().validate() + status().ifPresent { it.validate() } + type().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + /** The role of the message input. One of `user`, `system`, or `developer`. */ class Role @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -423,6 +445,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -531,6 +580,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -624,6 +700,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputText.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputText.kt index 26e5cb949..bd1be4a9e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputText.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputText.kt @@ -176,6 +176,24 @@ private constructor( 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("input_text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseItem.kt index e76b7e5d1..2ac308f03 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseItem.kt @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.BaseDeserializer import com.openai.core.BaseSerializer import com.openai.core.JsonValue +import com.openai.core.allMaxBy import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException import java.util.Objects @@ -134,8 +135,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { responseInputMessageItem != null -> visitor.visitResponseInputMessageItem(responseInputMessageItem) responseOutputMessage != null -> @@ -148,7 +149,6 @@ private constructor( functionCallOutput != null -> visitor.visitFunctionCallOutput(functionCallOutput) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -203,6 +203,55 @@ private constructor( 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 visitResponseInputMessageItem( + responseInputMessageItem: ResponseInputMessageItem + ) = responseInputMessageItem.validity() + + override fun visitResponseOutputMessage( + responseOutputMessage: ResponseOutputMessage + ) = responseOutputMessage.validity() + + override fun visitFileSearchCall(fileSearchCall: ResponseFileSearchToolCall) = + fileSearchCall.validity() + + override fun visitComputerCall(computerCall: ResponseComputerToolCall) = + computerCall.validity() + + override fun visitComputerCallOutput( + computerCallOutput: ResponseComputerToolCallOutputItem + ) = computerCallOutput.validity() + + override fun visitWebSearchCall(webSearchCall: ResponseFunctionWebSearch) = + webSearchCall.validity() + + override fun visitFunctionCall(functionCall: ResponseFunctionToolCallItem) = + functionCall.validity() + + override fun visitFunctionCallOutput( + functionCallOutput: ResponseFunctionToolCallOutputItem + ) = functionCallOutput.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -349,58 +398,65 @@ private constructor( when (type) { "message" -> { - tryDeserialize(node, jacksonTypeRef()) { - it.validate() - } - ?.let { - return ResponseItem(responseInputMessageItem = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return ResponseItem(responseOutputMessage = it, _json = json) - } + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { + ResponseItem(responseInputMessageItem = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ResponseItem(responseOutputMessage = 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 boolean). + 0 -> ResponseItem(_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() + } } "file_search_call" -> { - return ResponseItem( - fileSearchCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseItem(fileSearchCall = it, _json = json) + } ?: ResponseItem(_json = json) } "computer_call" -> { - return ResponseItem( - computerCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseItem(computerCall = it, _json = json) + } ?: ResponseItem(_json = json) } "computer_call_output" -> { - return ResponseItem( - computerCallOutput = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseItem(computerCallOutput = it, _json = json) } + ?: ResponseItem(_json = json) } "web_search_call" -> { - return ResponseItem( - webSearchCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseItem(webSearchCall = it, _json = json) + } ?: ResponseItem(_json = json) } "function_call" -> { - return ResponseItem( - functionCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseItem(functionCall = it, _json = json) } + ?: ResponseItem(_json = json) } "function_call_output" -> { - return ResponseItem( - functionCallOutput = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseItem(functionCallOutput = it, _json = json) } + ?: ResponseItem(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputAudio.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputAudio.kt index 962d1e297..6ca980b28 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputAudio.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputAudio.kt @@ -213,6 +213,25 @@ private constructor( 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 (data.asKnown().isPresent) 1 else 0) + + (if (transcript.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("output_audio")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt index 4fbb1bf7f..ec08f8055 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItem.kt @@ -118,8 +118,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { message != null -> visitor.visitMessage(message) fileSearchCall != null -> visitor.visitFileSearchCall(fileSearchCall) functionCall != null -> visitor.visitFunctionCall(functionCall) @@ -128,7 +128,6 @@ private constructor( reasoning != null -> visitor.visitReasoning(reasoning) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -167,6 +166,43 @@ private constructor( 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 visitMessage(message: ResponseOutputMessage) = message.validity() + + override fun visitFileSearchCall(fileSearchCall: ResponseFileSearchToolCall) = + fileSearchCall.validity() + + override fun visitFunctionCall(functionCall: ResponseFunctionToolCall) = + functionCall.validity() + + override fun visitWebSearchCall(webSearchCall: ResponseFunctionWebSearch) = + webSearchCall.validity() + + override fun visitComputerCall(computerCall: ResponseComputerToolCall) = + computerCall.validity() + + override fun visitReasoning(reasoning: ResponseReasoningItem) = reasoning.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -306,44 +342,34 @@ private constructor( when (type) { "message" -> { - return ResponseOutputItem( - message = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseOutputItem(message = it, _json = json) + } ?: ResponseOutputItem(_json = json) } "file_search_call" -> { - return ResponseOutputItem( - fileSearchCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseOutputItem(fileSearchCall = it, _json = json) + } ?: ResponseOutputItem(_json = json) } "function_call" -> { - return ResponseOutputItem( - functionCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseOutputItem(functionCall = it, _json = json) + } ?: ResponseOutputItem(_json = json) } "web_search_call" -> { - return ResponseOutputItem( - webSearchCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseOutputItem(webSearchCall = it, _json = json) + } ?: ResponseOutputItem(_json = json) } "computer_call" -> { - return ResponseOutputItem( - computerCall = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseOutputItem(computerCall = it, _json = json) + } ?: ResponseOutputItem(_json = json) } "reasoning" -> { - return ResponseOutputItem( - reasoning = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseOutputItem(reasoning = it, _json = json) + } ?: ResponseOutputItem(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemAddedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemAddedEvent.kt index b4351dcc2..3002e6673 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemAddedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemAddedEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when a new output item is added. */ class ResponseOutputItemAddedEvent @@ -238,6 +239,25 @@ private constructor( 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 = + (item.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.output_item.added")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemDoneEvent.kt index fe1d5b21d..763f3e300 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputItemDoneEvent.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Emitted when an output item is marked done. */ class ResponseOutputItemDoneEvent @@ -238,6 +239,25 @@ private constructor( 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 = + (item.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.output_item.done")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputMessage.kt index 065656cda..6be7c0ffa 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputMessage.kt @@ -326,7 +326,7 @@ private constructor( throw OpenAIInvalidDataException("'role' is invalid, received $it") } } - status() + status().validate() _type().let { if (it != JsonValue.from("message")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -335,6 +335,27 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + role.let { if (it == JsonValue.from("assistant")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("message")) 1 else 0 } + /** A text output from the model. */ @JsonDeserialize(using = Content.Deserializer::class) @JsonSerialize(using = Content.Serializer::class) @@ -363,13 +384,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { outputText != null -> visitor.visitOutputText(outputText) refusal != null -> visitor.visitRefusal(refusal) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -392,6 +412,33 @@ private constructor( 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 visitOutputText(outputText: ResponseOutputText) = + outputText.validity() + + override fun visitRefusal(refusal: ResponseOutputRefusal) = refusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -454,16 +501,14 @@ private constructor( when (type) { "output_text" -> { - return Content( - outputText = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Content(outputText = it, _json = json) + } ?: Content(_json = json) } "refusal" -> { - return Content( - refusal = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Content(refusal = it, _json = json) + } ?: Content(_json = json) } } @@ -583,6 +628,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputRefusal.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputRefusal.kt index b7a0f9f8a..9248f6450 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputRefusal.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputRefusal.kt @@ -176,6 +176,24 @@ private constructor( 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 (refusal.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("refusal")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputText.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputText.kt index eebeb18a5..fc1104171 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputText.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseOutputText.kt @@ -255,6 +255,25 @@ private constructor( 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 = + (annotations.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("output_text")) 1 else 0 } + /** A citation to a file. */ @JsonDeserialize(using = Annotation.Deserializer::class) @JsonSerialize(using = Annotation.Serializer::class) @@ -292,14 +311,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { fileCitation != null -> visitor.visitFileCitation(fileCitation) urlCitation != null -> visitor.visitUrlCitation(urlCitation) filePath != null -> visitor.visitFilePath(filePath) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -326,6 +344,35 @@ private constructor( 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 visitFileCitation(fileCitation: FileCitation) = + fileCitation.validity() + + override fun visitUrlCitation(urlCitation: UrlCitation) = urlCitation.validity() + + override fun visitFilePath(filePath: FilePath) = filePath.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -396,22 +443,19 @@ private constructor( when (type) { "file_citation" -> { - return Annotation( - fileCitation = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(fileCitation = it, _json = json) + } ?: Annotation(_json = json) } "url_citation" -> { - return Annotation( - urlCitation = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(urlCitation = it, _json = json) + } ?: Annotation(_json = json) } "file_path" -> { - return Annotation( - filePath = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(filePath = it, _json = json) + } ?: Annotation(_json = json) } } @@ -640,6 +684,26 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (if (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_citation")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -940,6 +1004,28 @@ private constructor( 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 (endIndex.asKnown().isPresent) 1 else 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (title.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("url_citation")) 1 else 0 } + + (if (url.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1162,6 +1248,26 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (if (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_path")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt index 8ad3d0e73..2b460f933 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseReasoningItem.kt @@ -18,6 +18,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** A description of the chain of thought used by a reasoning model while generating a response. */ class ResponseReasoningItem @@ -263,10 +264,30 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - status() + status().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (summary.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + type.let { if (it == JsonValue.from("reasoning")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + class Summary private constructor( private val text: JsonField, @@ -424,6 +445,25 @@ private constructor( 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("summary_text")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -537,6 +577,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDeltaEvent.kt index f2491c650..e1d724604 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDeltaEvent.kt @@ -286,6 +286,27 @@ private constructor( 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 (contentIndex.asKnown().isPresent) 1 else 0) + + (if (delta.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.refusal.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDoneEvent.kt index 8c0cb78df..a2b7199b6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRefusalDoneEvent.kt @@ -286,6 +286,27 @@ private constructor( 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 (contentIndex.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + (if (refusal.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.refusal.done")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStatus.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStatus.kt index 7768eeeab..9ae4d6d1c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStatus.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStatus.kt @@ -108,6 +108,32 @@ class ResponseStatus @JsonCreator private constructor(private val value: JsonFie fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ResponseStatus = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStreamEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStreamEvent.kt index 82ada6382..21d292e3c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStreamEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseStreamEvent.kt @@ -357,8 +357,8 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { audioDelta != null -> visitor.visitAudioDelta(audioDelta) audioDone != null -> visitor.visitAudioDone(audioDone) audioTranscriptDelta != null -> visitor.visitAudioTranscriptDelta(audioTranscriptDelta) @@ -407,7 +407,6 @@ private constructor( visitor.visitWebSearchCallSearching(webSearchCallSearching) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -584,6 +583,137 @@ private constructor( 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 visitAudioDelta(audioDelta: ResponseAudioDeltaEvent) = + audioDelta.validity() + + override fun visitAudioDone(audioDone: ResponseAudioDoneEvent) = + audioDone.validity() + + override fun visitAudioTranscriptDelta( + audioTranscriptDelta: ResponseAudioTranscriptDeltaEvent + ) = audioTranscriptDelta.validity() + + override fun visitAudioTranscriptDone( + audioTranscriptDone: ResponseAudioTranscriptDoneEvent + ) = audioTranscriptDone.validity() + + override fun visitCodeInterpreterCallCodeDelta( + codeInterpreterCallCodeDelta: ResponseCodeInterpreterCallCodeDeltaEvent + ) = codeInterpreterCallCodeDelta.validity() + + override fun visitCodeInterpreterCallCodeDone( + codeInterpreterCallCodeDone: ResponseCodeInterpreterCallCodeDoneEvent + ) = codeInterpreterCallCodeDone.validity() + + override fun visitCodeInterpreterCallCompleted( + codeInterpreterCallCompleted: ResponseCodeInterpreterCallCompletedEvent + ) = codeInterpreterCallCompleted.validity() + + override fun visitCodeInterpreterCallInProgress( + codeInterpreterCallInProgress: ResponseCodeInterpreterCallInProgressEvent + ) = codeInterpreterCallInProgress.validity() + + override fun visitCodeInterpreterCallInterpreting( + codeInterpreterCallInterpreting: ResponseCodeInterpreterCallInterpretingEvent + ) = codeInterpreterCallInterpreting.validity() + + override fun visitCompleted(completed: ResponseCompletedEvent) = + completed.validity() + + override fun visitContentPartAdded( + contentPartAdded: ResponseContentPartAddedEvent + ) = contentPartAdded.validity() + + override fun visitContentPartDone(contentPartDone: ResponseContentPartDoneEvent) = + contentPartDone.validity() + + override fun visitCreated(created: ResponseCreatedEvent) = created.validity() + + override fun visitError(error: ResponseErrorEvent) = error.validity() + + override fun visitFileSearchCallCompleted( + fileSearchCallCompleted: ResponseFileSearchCallCompletedEvent + ) = fileSearchCallCompleted.validity() + + override fun visitFileSearchCallInProgress( + fileSearchCallInProgress: ResponseFileSearchCallInProgressEvent + ) = fileSearchCallInProgress.validity() + + override fun visitFileSearchCallSearching( + fileSearchCallSearching: ResponseFileSearchCallSearchingEvent + ) = fileSearchCallSearching.validity() + + override fun visitFunctionCallArgumentsDelta( + functionCallArgumentsDelta: ResponseFunctionCallArgumentsDeltaEvent + ) = functionCallArgumentsDelta.validity() + + override fun visitFunctionCallArgumentsDone( + functionCallArgumentsDone: ResponseFunctionCallArgumentsDoneEvent + ) = functionCallArgumentsDone.validity() + + override fun visitInProgress(inProgress: ResponseInProgressEvent) = + inProgress.validity() + + override fun visitFailed(failed: ResponseFailedEvent) = failed.validity() + + override fun visitIncomplete(incomplete: ResponseIncompleteEvent) = + incomplete.validity() + + override fun visitOutputItemAdded(outputItemAdded: ResponseOutputItemAddedEvent) = + outputItemAdded.validity() + + override fun visitOutputItemDone(outputItemDone: ResponseOutputItemDoneEvent) = + outputItemDone.validity() + + override fun visitRefusalDelta(refusalDelta: ResponseRefusalDeltaEvent) = + refusalDelta.validity() + + override fun visitRefusalDone(refusalDone: ResponseRefusalDoneEvent) = + refusalDone.validity() + + override fun visitOutputTextAnnotationAdded( + outputTextAnnotationAdded: ResponseTextAnnotationDeltaEvent + ) = outputTextAnnotationAdded.validity() + + override fun visitOutputTextDelta(outputTextDelta: ResponseTextDeltaEvent) = + outputTextDelta.validity() + + override fun visitOutputTextDone(outputTextDone: ResponseTextDoneEvent) = + outputTextDone.validity() + + override fun visitWebSearchCallCompleted( + webSearchCallCompleted: ResponseWebSearchCallCompletedEvent + ) = webSearchCallCompleted.validity() + + override fun visitWebSearchCallInProgress( + webSearchCallInProgress: ResponseWebSearchCallInProgressEvent + ) = webSearchCallInProgress.validity() + + override fun visitWebSearchCallSearching( + webSearchCallSearching: ResponseWebSearchCallSearchingEvent + ) = webSearchCallSearching.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -972,257 +1102,208 @@ private constructor( when (type) { "response.audio.delta" -> { - return ResponseStreamEvent( - audioDelta = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(audioDelta = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.audio.done" -> { - return ResponseStreamEvent( - audioDone = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(audioDone = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.audio.transcript.delta" -> { - return ResponseStreamEvent( - audioTranscriptDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(audioTranscriptDelta = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.audio.transcript.done" -> { - return ResponseStreamEvent( - audioTranscriptDone = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(audioTranscriptDone = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.code_interpreter_call.code.delta" -> { - return ResponseStreamEvent( - codeInterpreterCallCodeDelta = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ResponseStreamEvent(codeInterpreterCallCodeDelta = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.code_interpreter_call.code.done" -> { - return ResponseStreamEvent( - codeInterpreterCallCodeDone = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ResponseStreamEvent(codeInterpreterCallCodeDone = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.code_interpreter_call.completed" -> { - return ResponseStreamEvent( - codeInterpreterCallCompleted = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ResponseStreamEvent(codeInterpreterCallCompleted = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.code_interpreter_call.in_progress" -> { - return ResponseStreamEvent( - codeInterpreterCallInProgress = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ResponseStreamEvent(codeInterpreterCallInProgress = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.code_interpreter_call.interpreting" -> { - return ResponseStreamEvent( - codeInterpreterCallInterpreting = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ResponseStreamEvent(codeInterpreterCallInterpreting = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.completed" -> { - return ResponseStreamEvent( - completed = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(completed = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.content_part.added" -> { - return ResponseStreamEvent( - contentPartAdded = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(contentPartAdded = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.content_part.done" -> { - return ResponseStreamEvent( - contentPartDone = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(contentPartDone = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.created" -> { - return ResponseStreamEvent( - created = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(created = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "error" -> { - return ResponseStreamEvent( - error = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(error = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.file_search_call.completed" -> { - return ResponseStreamEvent( - fileSearchCallCompleted = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(fileSearchCallCompleted = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.file_search_call.in_progress" -> { - return ResponseStreamEvent( - fileSearchCallInProgress = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(fileSearchCallInProgress = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.file_search_call.searching" -> { - return ResponseStreamEvent( - fileSearchCallSearching = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(fileSearchCallSearching = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.function_call_arguments.delta" -> { - return ResponseStreamEvent( - functionCallArgumentsDelta = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(functionCallArgumentsDelta = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.function_call_arguments.done" -> { - return ResponseStreamEvent( - functionCallArgumentsDone = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(functionCallArgumentsDone = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.in_progress" -> { - return ResponseStreamEvent( - inProgress = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(inProgress = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.failed" -> { - return ResponseStreamEvent( - failed = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(failed = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.incomplete" -> { - return ResponseStreamEvent( - incomplete = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(incomplete = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.output_item.added" -> { - return ResponseStreamEvent( - outputItemAdded = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(outputItemAdded = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.output_item.done" -> { - return ResponseStreamEvent( - outputItemDone = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(outputItemDone = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.refusal.delta" -> { - return ResponseStreamEvent( - refusalDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(refusalDelta = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.refusal.done" -> { - return ResponseStreamEvent( - refusalDone = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(refusalDone = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.output_text.annotation.added" -> { - return ResponseStreamEvent( - outputTextAnnotationAdded = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { ResponseStreamEvent(outputTextAnnotationAdded = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.output_text.delta" -> { - return ResponseStreamEvent( - outputTextDelta = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(outputTextDelta = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.output_text.done" -> { - return ResponseStreamEvent( - outputTextDone = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseStreamEvent(outputTextDone = it, _json = json) + } ?: ResponseStreamEvent(_json = json) } "response.web_search_call.completed" -> { - return ResponseStreamEvent( - webSearchCallCompleted = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(webSearchCallCompleted = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.web_search_call.in_progress" -> { - return ResponseStreamEvent( - webSearchCallInProgress = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(webSearchCallInProgress = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } "response.web_search_call.searching" -> { - return ResponseStreamEvent( - webSearchCallSearching = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { ResponseStreamEvent(webSearchCallSearching = it, _json = json) } + ?: ResponseStreamEvent(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEvent.kt index f73dd965d..ea3e48225 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEvent.kt @@ -358,6 +358,28 @@ private constructor( 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 = + (annotation.asKnown().getOrNull()?.validity() ?: 0) + + (if (annotationIndex.asKnown().isPresent) 1 else 0) + + (if (contentIndex.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.output_text.annotation.added")) 1 else 0 } + /** A citation to a file. */ @JsonDeserialize(using = Annotation.Deserializer::class) @JsonSerialize(using = Annotation.Serializer::class) @@ -395,14 +417,13 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { fileCitation != null -> visitor.visitFileCitation(fileCitation) urlCitation != null -> visitor.visitUrlCitation(urlCitation) filePath != null -> visitor.visitFilePath(filePath) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -429,6 +450,35 @@ private constructor( 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 visitFileCitation(fileCitation: FileCitation) = + fileCitation.validity() + + override fun visitUrlCitation(urlCitation: UrlCitation) = urlCitation.validity() + + override fun visitFilePath(filePath: FilePath) = filePath.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -499,22 +549,19 @@ private constructor( when (type) { "file_citation" -> { - return Annotation( - fileCitation = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(fileCitation = it, _json = json) + } ?: Annotation(_json = json) } "url_citation" -> { - return Annotation( - urlCitation = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(urlCitation = it, _json = json) + } ?: Annotation(_json = json) } "file_path" -> { - return Annotation( - filePath = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Annotation(filePath = it, _json = json) + } ?: Annotation(_json = json) } } @@ -743,6 +790,26 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (if (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_citation")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1043,6 +1110,28 @@ private constructor( 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 (endIndex.asKnown().isPresent) 1 else 0) + + (if (startIndex.asKnown().isPresent) 1 else 0) + + (if (title.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("url_citation")) 1 else 0 } + + (if (url.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1265,6 +1354,26 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (if (index.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("file_path")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextConfig.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextConfig.kt index fd8d933d4..67e322f0e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextConfig.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextConfig.kt @@ -16,6 +16,7 @@ import com.openai.models.ResponseFormatText import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Configuration options for a text response from the model. Can be plain text or structured JSON @@ -172,6 +173,21 @@ private constructor( 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 = (format.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDeltaEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDeltaEvent.kt index 6640080be..7baaccac8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDeltaEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDeltaEvent.kt @@ -286,6 +286,27 @@ private constructor( 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 (contentIndex.asKnown().isPresent) 1 else 0) + + (if (delta.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.output_text.delta")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDoneEvent.kt index 3741b0bfb..49a5c3a5e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseTextDoneEvent.kt @@ -286,6 +286,27 @@ private constructor( 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 (contentIndex.asKnown().isPresent) 1 else 0) + + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.output_text.done")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseUsage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseUsage.kt index 8f34abdbf..e1d52d757 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseUsage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseUsage.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * Represents token usage details including input tokens, output tokens, a breakdown of output @@ -316,6 +317,27 @@ private constructor( 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 (inputTokens.asKnown().isPresent) 1 else 0) + + (inputTokensDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputTokens.asKnown().isPresent) 1 else 0) + + (outputTokensDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (totalTokens.asKnown().isPresent) 1 else 0) + /** A detailed breakdown of the input tokens. */ class InputTokensDetails private constructor( @@ -452,6 +474,23 @@ private constructor( 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 (cachedTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -603,6 +642,23 @@ private constructor( 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 (reasoningTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEvent.kt index 562e8de4b..fd6082452 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEvent.kt @@ -217,6 +217,25 @@ private constructor( 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 (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.web_search_call.completed")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEvent.kt index 5ee6ff48d..bee571bda 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEvent.kt @@ -217,6 +217,25 @@ private constructor( 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 (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.web_search_call.in_progress")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEvent.kt index efae2c727..b9c7eb84c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEvent.kt @@ -217,6 +217,25 @@ private constructor( 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 (itemId.asKnown().isPresent) 1 else 0) + + (if (outputIndex.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("response.web_search_call.searching")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt index fc6b42a6e..26b0d5304 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/Tool.kt @@ -91,15 +91,14 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { fileSearch != null -> visitor.visitFileSearch(fileSearch) function != null -> visitor.visitFunction(function) computerUsePreview != null -> visitor.visitComputerUsePreview(computerUsePreview) webSearch != null -> visitor.visitWebSearch(webSearch) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -130,6 +129,36 @@ private constructor( 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 visitFileSearch(fileSearch: FileSearchTool) = fileSearch.validity() + + override fun visitFunction(function: FunctionTool) = function.validity() + + override fun visitComputerUsePreview(computerUsePreview: ComputerTool) = + computerUsePreview.validity() + + override fun visitWebSearch(webSearch: WebSearchTool) = webSearch.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -228,31 +257,25 @@ private constructor( when (type) { "file_search" -> { - return Tool( - fileSearch = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Tool(fileSearch = it, _json = json) + } ?: Tool(_json = json) } "function" -> { - return Tool( - function = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Tool(function = it, _json = json) + } ?: Tool(_json = json) } "computer_use_preview" -> { - return Tool( - computerUsePreview = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef())?.let { + Tool(computerUsePreview = it, _json = json) + } ?: Tool(_json = json) } } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Tool(webSearch = it, _json = json) - } - - return Tool(_json = json) + return tryDeserialize(node, jacksonTypeRef())?.let { + Tool(webSearch = it, _json = json) + } ?: Tool(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceFunction.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceFunction.kt index 3b5b500a1..577ec3015 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceFunction.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceFunction.kt @@ -176,6 +176,24 @@ private constructor( 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 (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("function")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceOptions.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceOptions.kt index 2751460a8..2f81a4717 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceOptions.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceOptions.kt @@ -109,6 +109,32 @@ class ToolChoiceOptions @JsonCreator private constructor(private val value: Json fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): ToolChoiceOptions = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceTypes.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceTypes.kt index d04a729eb..357bd17ba 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceTypes.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ToolChoiceTypes.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * Indicates that the model should use a built-in tool to generate a response. @@ -150,10 +151,25 @@ private constructor( return@apply } - type() + type().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 = (type.asKnown().getOrNull()?.validity() ?: 0) + /** * The type of hosted tool the model should to use. Learn more about * [built-in tools](https://platform.openai.com/docs/guides/tools). @@ -260,6 +276,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt index 2f67d0da5..f8632d847 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/WebSearchTool.kt @@ -233,12 +233,31 @@ private constructor( return@apply } - type() - searchContextSize() + type().validate() + searchContextSize().ifPresent { it.validate() } userLocation().ifPresent { it.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 = + (type.asKnown().getOrNull()?.validity() ?: 0) + + (searchContextSize.asKnown().getOrNull()?.validity() ?: 0) + + (userLocation.asKnown().getOrNull()?.validity() ?: 0) + /** * The type of the web search tool. One of: * - `web_search_preview` @@ -329,6 +348,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -441,6 +487,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): SearchContextSize = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -708,6 +781,28 @@ private constructor( 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 = + type.let { if (it == JsonValue.from("approximate")) 1 else 0 } + + (if (city.asKnown().isPresent) 1 else 0) + + (if (country.asKnown().isPresent) 1 else 0) + + (if (region.asKnown().isPresent) 1 else 0) + + (if (timezone.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt index 77c54c314..112d6d27f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.responses.ResponseComputerToolCall import com.openai.models.responses.ResponseComputerToolCallOutputItem import com.openai.models.responses.ResponseFileSearchToolCall @@ -169,6 +170,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt index 8ac8535ca..99a4e0df7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.responses.ResponseComputerToolCall import com.openai.models.responses.ResponseComputerToolCallOutputItem import com.openai.models.responses.ResponseFileSearchToolCall @@ -171,6 +172,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt index c33009be7..11ddf361f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt @@ -392,6 +392,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/ResponseItemList.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/ResponseItemList.kt index 58a9af373..e3768ce60 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/ResponseItemList.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/ResponseItemList.kt @@ -25,6 +25,7 @@ import com.openai.models.responses.ResponseItem import com.openai.models.responses.ResponseOutputMessage import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** A list of Response items. */ class ResponseItemList @@ -348,6 +349,27 @@ private constructor( 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 = + (data.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (firstId.asKnown().isPresent) 1 else 0) + + (if (hasMore.asKnown().isPresent) 1 else 0) + + (if (lastId.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("list")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/Upload.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/Upload.kt index de52b8516..dd9817a9b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/Upload.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/Upload.kt @@ -431,11 +431,36 @@ private constructor( } } purpose() - status() + status().validate() file().ifPresent { it.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 = + (if (id.asKnown().isPresent) 1 else 0) + + (if (bytes.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("upload")) 1 else 0 } + + (if (purpose.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (file.asKnown().getOrNull()?.validity() ?: 0) + /** The status of the Upload. */ class Status @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -534,6 +559,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt index e7d996825..cc4d34350 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt @@ -20,6 +20,7 @@ import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). @@ -475,6 +476,24 @@ private constructor( 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 = + (partIds.asKnown().getOrNull()?.size ?: 0) + (if (md5.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCreateParams.kt index ebb255134..1b72bc294 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCreateParams.kt @@ -18,6 +18,7 @@ import com.openai.errors.OpenAIInvalidDataException import com.openai.models.files.FilePurpose import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** * Creates an intermediate [Upload](https://platform.openai.com/docs/api-reference/uploads/object) @@ -595,10 +596,31 @@ private constructor( bytes() filename() mimeType() - purpose() + purpose().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 = + (if (bytes.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + (if (mimeType.asKnown().isPresent) 1 else 0) + + (purpose.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt index 908270cee..c8739566a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt @@ -330,6 +330,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/UploadPart.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/UploadPart.kt index 9e6d11bba..ebad04272 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/UploadPart.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/UploadPart.kt @@ -244,6 +244,26 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("upload.part")) 1 else 0 } + + (if (uploadId.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParam.kt index 82f404cf4..094ed9261 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParam.kt @@ -131,6 +131,22 @@ private constructor( 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 = type.let { if (it == JsonValue.from("auto")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategy.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategy.kt index c73bc4341..6fdb1c613 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategy.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategy.kt @@ -50,13 +50,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { static_ != null -> visitor.visitStatic(static_) other != null -> visitor.visitOther(other) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -79,6 +78,32 @@ private constructor( 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 visitStatic(static_: StaticFileChunkingStrategyObject) = + static_.validity() + + override fun visitOther(other: OtherFileChunkingStrategyObject) = other.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -149,18 +174,14 @@ private constructor( when (type) { "static" -> { - return FileChunkingStrategy( - static_ = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { FileChunkingStrategy(static_ = it, _json = json) } + ?: FileChunkingStrategy(_json = json) } "other" -> { - return FileChunkingStrategy( - other = - deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { FileChunkingStrategy(other = it, _json = json) } + ?: FileChunkingStrategy(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParam.kt index 4eb52efd9..db3e8b62e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParam.kt @@ -55,13 +55,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { auto != null -> visitor.visitAuto(auto) static_ != null -> visitor.visitStatic(static_) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -84,6 +83,32 @@ private constructor( 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 visitAuto(auto: AutoFileChunkingStrategyParam) = auto.validity() + + override fun visitStatic(static_: StaticFileChunkingStrategyObjectParam) = + static_.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -156,20 +181,17 @@ private constructor( when (type) { "auto" -> { - return FileChunkingStrategyParam( - auto = deserialize(node, jacksonTypeRef()), - _json = json, - ) + return tryDeserialize(node, jacksonTypeRef()) + ?.let { FileChunkingStrategyParam(auto = it, _json = json) } + ?: FileChunkingStrategyParam(_json = json) } "static" -> { - return FileChunkingStrategyParam( - static_ = - deserialize( - node, - jacksonTypeRef(), - ), - _json = json, - ) + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { FileChunkingStrategyParam(static_ = it, _json = json) } + ?: FileChunkingStrategyParam(_json = json) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObject.kt index 9d18f780c..9f9af7c8e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObject.kt @@ -133,6 +133,22 @@ private constructor( 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 = type.let { if (it == JsonValue.from("other")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategy.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategy.kt index 6ef8513c8..9f3ab525a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategy.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategy.kt @@ -200,6 +200,24 @@ private constructor( 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 (chunkOverlapTokens.asKnown().isPresent) 1 else 0) + + (if (maxChunkSizeTokens.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObject.kt index a0403bee3..7ced1e0e4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObject.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class StaticFileChunkingStrategyObject private constructor( @@ -182,6 +183,24 @@ private constructor( 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 = + (static_.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("static")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParam.kt index d09360970..f6a84b9b3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParam.kt @@ -14,6 +14,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** Customize your own chunking strategy by setting chunk size and chunk overlap. */ class StaticFileChunkingStrategyObjectParam @@ -184,6 +185,24 @@ private constructor( 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 = + (static_.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("static")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStore.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStore.kt index 1bb98f59e..652d90bf8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStore.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStore.kt @@ -544,13 +544,40 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - status() + status().validate() usageBytes() expiresAfter().ifPresent { it.validate() } expiresAt() 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (fileCounts.asKnown().getOrNull()?.validity() ?: 0) + + (if (lastActiveAt.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("vector_store")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (usageBytes.asKnown().isPresent) 1 else 0) + + (expiresAfter.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + class FileCounts private constructor( private val cancelled: JsonField, @@ -821,6 +848,28 @@ private constructor( 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 (cancelled.asKnown().isPresent) 1 else 0) + + (if (completed.asKnown().isPresent) 1 else 0) + + (if (failed.asKnown().isPresent) 1 else 0) + + (if (inProgress.asKnown().isPresent) 1 else 0) + + (if (total.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -913,6 +962,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1025,6 +1092,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1201,6 +1295,25 @@ private constructor( 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 = + anchor.let { if (it == JsonValue.from("last_active_at")) 1 else 0 } + + (if (days.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreCreateParams.kt index c09d11b75..37df3d3a4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreCreateParams.kt @@ -717,6 +717,28 @@ private constructor( 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 = + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + + (expiresAfter.asKnown().getOrNull()?.validity() ?: 0) + + (fileIds.asKnown().getOrNull()?.size ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -898,6 +920,25 @@ private constructor( 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 = + anchor.let { if (it == JsonValue.from("last_active_at")) 1 else 0 } + + (if (days.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -990,6 +1031,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleted.kt index 67c4f1f77..0c6dca6a4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleted.kt @@ -201,6 +201,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("vector_store.deleted")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt index 0555dff80..13704c3d5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.VectorStoreService import java.util.Collections import java.util.Objects @@ -117,6 +118,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt index ade3c9fe9..025720494 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.VectorStoreServiceAsync import java.util.Collections import java.util.Objects @@ -119,6 +120,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListParams.kt index f7848243b..5432dcc39 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListParams.kt @@ -347,6 +347,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt index 18a137602..ecf280016 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.VectorStoreService import java.util.Collections import java.util.Objects @@ -115,6 +116,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt index e09209dd1..d70745bbe 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.VectorStoreServiceAsync import java.util.Collections import java.util.Objects @@ -117,6 +118,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt index 83733521a..a9abb4d41 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -31,6 +32,7 @@ import com.openai.models.CompoundFilter import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Search a vector store for relevant chunks based on a query and file attributes filter. */ class VectorStoreSearchParams @@ -689,6 +691,28 @@ private constructor( 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 = + (query.asKnown().getOrNull()?.validity() ?: 0) + + (filters.asKnown().getOrNull()?.validity() ?: 0) + + (if (maxNumResults.asKnown().isPresent) 1 else 0) + + (rankingOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (rewriteQuery.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -731,13 +755,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { string != null -> visitor.visitString(string) strings != null -> visitor.visitStrings(strings) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -756,6 +779,32 @@ private constructor( 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 visitString(string: String) = 1 + + override fun visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -808,14 +857,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Query { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef())?.let { - return Query(string = it, _json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Query(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Query(strings = 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 object). + 0 -> Query(_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() } - tryDeserialize(node, jacksonTypeRef>())?.let { - return Query(strings = it, _json = json) - } - - return Query(_json = json) } } @@ -870,13 +933,12 @@ private constructor( fun _json(): Optional = Optional.ofNullable(_json) - fun accept(visitor: Visitor): T { - return when { + fun accept(visitor: Visitor): T = + when { comparisonFilter != null -> visitor.visitComparisonFilter(comparisonFilter) compoundFilter != null -> visitor.visitCompoundFilter(compoundFilter) else -> visitor.unknown(_json) } - } private var validated: Boolean = false @@ -899,6 +961,34 @@ private constructor( 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 visitComparisonFilter(comparisonFilter: ComparisonFilter) = + comparisonFilter.validity() + + override fun visitCompoundFilter(compoundFilter: CompoundFilter) = + compoundFilter.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -967,16 +1057,28 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): Filters { val json = JsonValue.fromJsonNode(node) - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Filters(comparisonFilter = it, _json = json) - } - tryDeserialize(node, jacksonTypeRef()) { it.validate() } - ?.let { - return Filters(compoundFilter = it, _json = json) - } - - return Filters(_json = json) + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Filters(comparisonFilter = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Filters(compoundFilter = 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 boolean). + 0 -> Filters(_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() + } } } @@ -1135,11 +1237,30 @@ private constructor( return@apply } - ranker() + ranker().ifPresent { it.validate() } scoreThreshold() 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 = + (ranker.asKnown().getOrNull()?.validity() ?: 0) + + (if (scoreThreshold.asKnown().isPresent) 1 else 0) + class Ranker @JsonCreator private constructor(private val value: JsonField) : Enum { /** @@ -1229,6 +1350,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Ranker = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponse.kt index 8ce9a7515..a22fc8ef4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponse.kt @@ -314,6 +314,27 @@ private constructor( 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 = + (attributes.asKnown().getOrNull()?.validity() ?: 0) + + (content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + (if (score.asKnown().isPresent) 1 else 0) + /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing * additional information about the object in a structured format, and querying for objects via @@ -386,6 +407,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -558,10 +597,28 @@ private constructor( } text() - type() + type().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 = + (if (text.asKnown().isPresent) 1 else 0) + (type.asKnown().getOrNull()?.validity() ?: 0) + /** The type of content. */ class Type @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -644,6 +701,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt index 03c7ee157..e4c1be134 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt @@ -529,6 +529,26 @@ private constructor( 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 = + (expiresAfter.asKnown().getOrNull()?.validity() ?: 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -710,6 +730,25 @@ private constructor( 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 = + anchor.let { if (it == JsonValue.from("last_active_at")) 1 else 0 } + + (if (days.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -802,6 +841,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt index 313813d9d..74f419294 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt @@ -634,6 +634,26 @@ private constructor( 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 = + (fileIds.asKnown().getOrNull()?.size ?: 0) + + (attributes.asKnown().getOrNull()?.validity() ?: 0) + + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -724,6 +744,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt index aaa25990b..d8da1f3ca 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.vectorstores.files.VectorStoreFile import com.openai.services.blocking.vectorstores.FileBatchService import java.util.Collections @@ -118,6 +119,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt index e0a9c5cea..4cff8d389 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.vectorstores.files.VectorStoreFile import com.openai.services.async.vectorstores.FileBatchServiceAsync import java.util.Collections @@ -120,6 +121,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt index 0240a987e..2be57ce8b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt @@ -408,6 +408,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Filter = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -510,6 +537,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatch.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatch.kt index bd3b3b143..823ac3ed7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatch.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatch.kt @@ -15,6 +15,7 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull /** A batch of files attached to a vector store. */ class VectorStoreFileBatch @@ -327,11 +328,33 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - status() + status().validate() vectorStoreId() 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (fileCounts.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("vector_store.files_batch")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (vectorStoreId.asKnown().isPresent) 1 else 0) + class FileCounts private constructor( private val cancelled: JsonField, @@ -602,6 +625,28 @@ private constructor( 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 (cancelled.asKnown().isPresent) 1 else 0) + + (if (completed.asKnown().isPresent) 1 else 0) + + (if (failed.asKnown().isPresent) 1 else 0) + + (if (inProgress.asKnown().isPresent) 1 else 0) + + (if (total.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -721,6 +766,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt index 174c03e8b..39fe8be21 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.vectorstores.FileService import java.util.Collections import java.util.Objects @@ -110,6 +111,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt index dad74f86d..5749fc805 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.vectorstores.FileServiceAsync import java.util.Collections import java.util.Objects @@ -112,6 +113,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentResponse.kt index fade507bf..552a0d49f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentResponse.kt @@ -152,6 +152,23 @@ private constructor( 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) + (if (type.asKnown().isPresent) 1 else 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt index 9d64c73ba..01567fa7a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt @@ -613,6 +613,26 @@ private constructor( 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 (fileId.asKnown().isPresent) 1 else 0) + + (attributes.asKnown().getOrNull()?.validity() ?: 0) + + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -703,6 +723,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt index 24cdb601b..348db62c3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.blocking.vectorstores.FileService import java.util.Collections import java.util.Objects @@ -114,6 +115,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt index b90239d21..fe7c52afe 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt @@ -10,6 +10,7 @@ import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException import com.openai.services.async.vectorstores.FileServiceAsync import java.util.Collections import java.util.Objects @@ -116,6 +117,14 @@ private constructor( validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + fun toBuilder() = Builder().from(this) override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt index a6e0e2649..4f0240de9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt @@ -397,6 +397,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Filter = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -499,6 +526,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Order = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt index d2cf152cc..e6237fdee 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt @@ -414,6 +414,23 @@ private constructor( 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 = (attributes.asKnown().getOrNull()?.validity() ?: 0) + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -504,6 +521,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFile.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFile.kt index 53a27633b..b8b685500 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFile.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFile.kt @@ -500,7 +500,7 @@ private constructor( throw OpenAIInvalidDataException("'object_' is invalid, received $it") } } - status() + status().validate() usageBytes() vectorStoreId() attributes().ifPresent { it.validate() } @@ -508,6 +508,31 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (lastError.asKnown().getOrNull()?.validity() ?: 0) + + object_.let { if (it == JsonValue.from("vector_store.file")) 1 else 0 } + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (usageBytes.asKnown().isPresent) 1 else 0) + + (if (vectorStoreId.asKnown().isPresent) 1 else 0) + + (attributes.asKnown().getOrNull()?.validity() ?: 0) + + (chunkingStrategy.asKnown().getOrNull()?.validity() ?: 0) + /** * The last error associated with this vector store file. Will be `null` if there are no errors. */ @@ -665,11 +690,30 @@ private constructor( return@apply } - code() + code().validate() message() 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 = + (code.asKnown().getOrNull()?.validity() ?: 0) + + (if (message.asKnown().isPresent) 1 else 0) + /** One of `server_error` or `rate_limit_exceeded`. */ class Code @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -764,6 +808,33 @@ private constructor( OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Code = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -897,6 +968,33 @@ private constructor( fun asString(): String = _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -982,6 +1080,24 @@ private constructor( 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeleted.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeleted.kt index 442bb2590..5ae0e8fd1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeleted.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeleted.kt @@ -201,6 +201,25 @@ private constructor( 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 (id.asKnown().isPresent) 1 else 0) + + (if (deleted.asKnown().isPresent) 1 else 0) + + object_.let { if (it == JsonValue.from("vector_store.file.deleted")) 1 else 0 } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/test/kotlin/com/openai/models/AllModelsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/AllModelsTest.kt index 623652142..f556f9e78 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/AllModelsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/AllModelsTest.kt @@ -2,8 +2,15 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class AllModelsTest { @@ -18,6 +25,20 @@ internal class AllModelsTest { assertThat(allModels.unionMember2()).isEmpty } + @Test + fun ofStringRoundtrip() { + val jsonMapper = jsonMapper() + val allModels = AllModels.ofString("string") + + val roundtrippedAllModels = + jsonMapper.readValue( + jsonMapper.writeValueAsString(allModels), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAllModels).isEqualTo(allModels) + } + @Test fun ofChatModel() { val chatModel = ChatModel.O3_MINI @@ -29,6 +50,20 @@ internal class AllModelsTest { assertThat(allModels.unionMember2()).isEmpty } + @Test + fun ofChatModelRoundtrip() { + val jsonMapper = jsonMapper() + val allModels = AllModels.ofChatModel(ChatModel.O3_MINI) + + val roundtrippedAllModels = + jsonMapper.readValue( + jsonMapper.writeValueAsString(allModels), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAllModels).isEqualTo(allModels) + } + @Test fun ofUnionMember2() { val unionMember2 = AllModels.UnionMember2.O1_PRO @@ -39,4 +74,32 @@ internal class AllModelsTest { assertThat(allModels.chatModel()).isEmpty assertThat(allModels.unionMember2()).contains(unionMember2) } + + @Test + fun ofUnionMember2Roundtrip() { + val jsonMapper = jsonMapper() + val allModels = AllModels.ofUnionMember2(AllModels.UnionMember2.O1_PRO) + + val roundtrippedAllModels = + jsonMapper.readValue( + jsonMapper.writeValueAsString(allModels), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAllModels).isEqualTo(allModels) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + OBJECT(JsonValue.from(mapOf("invalid" to "object"))), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val allModels = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { allModels.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ComparisonFilterTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ComparisonFilterTest.kt index b712f7e71..07f469205 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ComparisonFilterTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ComparisonFilterTest.kt @@ -2,6 +2,8 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -20,4 +22,23 @@ internal class ComparisonFilterTest { assertThat(comparisonFilter.type()).isEqualTo(ComparisonFilter.Type.EQ) assertThat(comparisonFilter.value()).isEqualTo(ComparisonFilter.Value.ofString("string")) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val comparisonFilter = + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + + val roundtrippedComparisonFilter = + jsonMapper.readValue( + jsonMapper.writeValueAsString(comparisonFilter), + jacksonTypeRef(), + ) + + assertThat(roundtrippedComparisonFilter).isEqualTo(comparisonFilter) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/CompoundFilterTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/CompoundFilterTest.kt index dc3a5f53d..0f8ecf710 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/CompoundFilterTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/CompoundFilterTest.kt @@ -2,6 +2,8 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,28 @@ internal class CompoundFilterTest { ) assertThat(compoundFilter.type()).isEqualTo(CompoundFilter.Type.AND) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val compoundFilter = + CompoundFilter.builder() + .addFilter( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .type(CompoundFilter.Type.AND) + .build() + + val roundtrippedCompoundFilter = + jsonMapper.readValue( + jsonMapper.writeValueAsString(compoundFilter), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCompoundFilter).isEqualTo(compoundFilter) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ErrorObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ErrorObjectTest.kt index 8d51e4cae..92d999536 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ErrorObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ErrorObjectTest.kt @@ -2,6 +2,8 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,24 @@ internal class ErrorObjectTest { assertThat(errorObject.param()).contains("param") assertThat(errorObject.type()).isEqualTo("type") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val errorObject = + ErrorObject.builder() + .code("code") + .message("message") + .param("param") + .type("type") + .build() + + val roundtrippedErrorObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(errorObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedErrorObject).isEqualTo(errorObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/FunctionDefinitionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/FunctionDefinitionTest.kt index 00c55ff92..5ac722e48 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/FunctionDefinitionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/FunctionDefinitionTest.kt @@ -2,7 +2,9 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -32,4 +34,28 @@ internal class FunctionDefinitionTest { ) assertThat(functionDefinition.strict()).contains(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val functionDefinition = + FunctionDefinition.builder() + .name("name") + .description("description") + .parameters( + FunctionParameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + + val roundtrippedFunctionDefinition = + jsonMapper.readValue( + jsonMapper.writeValueAsString(functionDefinition), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFunctionDefinition).isEqualTo(functionDefinition) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/FunctionParametersTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/FunctionParametersTest.kt index 5806a2724..bd96cc44d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/FunctionParametersTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/FunctionParametersTest.kt @@ -2,7 +2,10 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class FunctionParametersTest { @@ -12,4 +15,19 @@ internal class FunctionParametersTest { val functionParameters = FunctionParameters.builder().putAdditionalProperty("foo", JsonValue.from("bar")).build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val functionParameters = + FunctionParameters.builder().putAdditionalProperty("foo", JsonValue.from("bar")).build() + + val roundtrippedFunctionParameters = + jsonMapper.readValue( + jsonMapper.writeValueAsString(functionParameters), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFunctionParameters).isEqualTo(functionParameters) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ReasoningTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ReasoningTest.kt index 22f34a0b6..8e923c9b9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ReasoningTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ReasoningTest.kt @@ -2,6 +2,8 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,4 +20,22 @@ internal class ReasoningTest { assertThat(reasoning.effort()).contains(ReasoningEffort.LOW) assertThat(reasoning.generateSummary()).contains(Reasoning.GenerateSummary.CONCISE) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val reasoning = + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + + val roundtrippedReasoning = + jsonMapper.readValue( + jsonMapper.writeValueAsString(reasoning), + jacksonTypeRef(), + ) + + assertThat(roundtrippedReasoning).isEqualTo(reasoning) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonObjectTest.kt index c50a978e7..0e38beb4b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonObjectTest.kt @@ -2,6 +2,9 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class ResponseFormatJsonObjectTest { @@ -10,4 +13,18 @@ internal class ResponseFormatJsonObjectTest { fun create() { val responseFormatJsonObject = ResponseFormatJsonObject.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFormatJsonObject = ResponseFormatJsonObject.builder().build() + + val roundtrippedResponseFormatJsonObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatJsonObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatJsonObject).isEqualTo(responseFormatJsonObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonSchemaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonSchemaTest.kt index cb2bcb891..b7edce10d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonSchemaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatJsonSchemaTest.kt @@ -2,7 +2,9 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -40,4 +42,32 @@ internal class ResponseFormatJsonSchemaTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFormatJsonSchema = + ResponseFormatJsonSchema.builder() + .jsonSchema( + ResponseFormatJsonSchema.JsonSchema.builder() + .name("name") + .description("description") + .schema( + ResponseFormatJsonSchema.JsonSchema.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) + .build() + + val roundtrippedResponseFormatJsonSchema = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatJsonSchema), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatJsonSchema).isEqualTo(responseFormatJsonSchema) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatTextTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatTextTest.kt index 2a4d5a3a5..89350ecaf 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatTextTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ResponseFormatTextTest.kt @@ -2,6 +2,9 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class ResponseFormatTextTest { @@ -10,4 +13,18 @@ internal class ResponseFormatTextTest { fun create() { val responseFormatText = ResponseFormatText.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFormatText = ResponseFormatText.builder().build() + + val roundtrippedResponseFormatText = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatText), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatText).isEqualTo(responseFormatText) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ResponsesModelTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ResponsesModelTest.kt index 2e1507174..f486eb36e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/ResponsesModelTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/ResponsesModelTest.kt @@ -2,8 +2,15 @@ package com.openai.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponsesModelTest { @@ -18,6 +25,20 @@ internal class ResponsesModelTest { assertThat(responsesModel.unionMember2()).isEmpty } + @Test + fun ofStringRoundtrip() { + val jsonMapper = jsonMapper() + val responsesModel = ResponsesModel.ofString("string") + + val roundtrippedResponsesModel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responsesModel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponsesModel).isEqualTo(responsesModel) + } + @Test fun ofChat() { val chat = ChatModel.O3_MINI @@ -29,6 +50,20 @@ internal class ResponsesModelTest { assertThat(responsesModel.unionMember2()).isEmpty } + @Test + fun ofChatRoundtrip() { + val jsonMapper = jsonMapper() + val responsesModel = ResponsesModel.ofChat(ChatModel.O3_MINI) + + val roundtrippedResponsesModel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responsesModel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponsesModel).isEqualTo(responsesModel) + } + @Test fun ofUnionMember2() { val unionMember2 = ResponsesModel.UnionMember2.O1_PRO @@ -39,4 +74,33 @@ internal class ResponsesModelTest { assertThat(responsesModel.chat()).isEmpty assertThat(responsesModel.unionMember2()).contains(unionMember2) } + + @Test + fun ofUnionMember2Roundtrip() { + val jsonMapper = jsonMapper() + val responsesModel = ResponsesModel.ofUnionMember2(ResponsesModel.UnionMember2.O1_PRO) + + val roundtrippedResponsesModel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responsesModel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponsesModel).isEqualTo(responsesModel) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + OBJECT(JsonValue.from(mapOf("invalid" to "object"))), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responsesModel = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responsesModel.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt index c60e39a10..0a7c3f77f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt @@ -2,14 +2,27 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class TranscriptionCreateResponseTest { @Test fun ofTranscription() { - val transcription = Transcription.builder().text("text").build() + val transcription = + Transcription.builder() + .text("text") + .addLogprob( + Transcription.Logprob.builder().token("token").addByte(0.0).logprob(0.0).build() + ) + .build() val transcriptionCreateResponse = TranscriptionCreateResponse.ofTranscription(transcription) @@ -17,14 +30,113 @@ internal class TranscriptionCreateResponseTest { assertThat(transcriptionCreateResponse.verbose()).isEmpty } + @Test + fun ofTranscriptionRoundtrip() { + val jsonMapper = jsonMapper() + val transcriptionCreateResponse = + TranscriptionCreateResponse.ofTranscription( + Transcription.builder() + .text("text") + .addLogprob( + Transcription.Logprob.builder() + .token("token") + .addByte(0.0) + .logprob(0.0) + .build() + ) + .build() + ) + + val roundtrippedTranscriptionCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionCreateResponse).isEqualTo(transcriptionCreateResponse) + } + @Test fun ofVerbose() { val verbose = - TranscriptionVerbose.builder().duration(0.0).language("language").text("text").build() + TranscriptionVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .build() val transcriptionCreateResponse = TranscriptionCreateResponse.ofVerbose(verbose) assertThat(transcriptionCreateResponse.transcription()).isEmpty assertThat(transcriptionCreateResponse.verbose()).contains(verbose) } + + @Test + fun ofVerboseRoundtrip() { + val jsonMapper = jsonMapper() + val transcriptionCreateResponse = + TranscriptionCreateResponse.ofVerbose( + TranscriptionVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .build() + ) + + val roundtrippedTranscriptionCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionCreateResponse).isEqualTo(transcriptionCreateResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val transcriptionCreateResponse = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { transcriptionCreateResponse.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt index 58bb4846d..127ee6b96 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt @@ -2,6 +2,8 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -34,4 +36,30 @@ internal class TranscriptionSegmentTest { assertThat(transcriptionSegment.text()).isEqualTo("text") assertThat(transcriptionSegment.tokens()).containsExactly(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transcriptionSegment = + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + + val roundtrippedTranscriptionSegment = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionSegment), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionSegment).isEqualTo(transcriptionSegment) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEventTest.kt index 2f19c7cde..b47e3fc59 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionStreamEventTest.kt @@ -2,14 +2,31 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class TranscriptionStreamEventTest { @Test fun ofTranscriptTextDelta() { - val transcriptTextDelta = TranscriptionTextDeltaEvent.builder().delta("delta").build() + val transcriptTextDelta = + TranscriptionTextDeltaEvent.builder() + .delta("delta") + .addLogprob( + TranscriptionTextDeltaEvent.Logprob.builder() + .token("token") + .addByte(JsonValue.from(mapOf())) + .logprob(0.0) + .build() + ) + .build() val transcriptionStreamEvent = TranscriptionStreamEvent.ofTranscriptTextDelta(transcriptTextDelta) @@ -18,9 +35,45 @@ internal class TranscriptionStreamEventTest { assertThat(transcriptionStreamEvent.transcriptTextDone()).isEmpty } + @Test + fun ofTranscriptTextDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val transcriptionStreamEvent = + TranscriptionStreamEvent.ofTranscriptTextDelta( + TranscriptionTextDeltaEvent.builder() + .delta("delta") + .addLogprob( + TranscriptionTextDeltaEvent.Logprob.builder() + .token("token") + .addByte(JsonValue.from(mapOf())) + .logprob(0.0) + .build() + ) + .build() + ) + + val roundtrippedTranscriptionStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionStreamEvent).isEqualTo(transcriptionStreamEvent) + } + @Test fun ofTranscriptTextDone() { - val transcriptTextDone = TranscriptionTextDoneEvent.builder().text("text").build() + val transcriptTextDone = + TranscriptionTextDoneEvent.builder() + .text("text") + .addLogprob( + TranscriptionTextDoneEvent.Logprob.builder() + .token("token") + .addByte(JsonValue.from(mapOf())) + .logprob(0.0) + .build() + ) + .build() val transcriptionStreamEvent = TranscriptionStreamEvent.ofTranscriptTextDone(transcriptTextDone) @@ -28,4 +81,48 @@ internal class TranscriptionStreamEventTest { assertThat(transcriptionStreamEvent.transcriptTextDelta()).isEmpty assertThat(transcriptionStreamEvent.transcriptTextDone()).contains(transcriptTextDone) } + + @Test + fun ofTranscriptTextDoneRoundtrip() { + val jsonMapper = jsonMapper() + val transcriptionStreamEvent = + TranscriptionStreamEvent.ofTranscriptTextDone( + TranscriptionTextDoneEvent.builder() + .text("text") + .addLogprob( + TranscriptionTextDoneEvent.Logprob.builder() + .token("token") + .addByte(JsonValue.from(mapOf())) + .logprob(0.0) + .build() + ) + .build() + ) + + val roundtrippedTranscriptionStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionStreamEvent).isEqualTo(transcriptionStreamEvent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val transcriptionStreamEvent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { transcriptionStreamEvent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTest.kt index 20123dfec..d4141bbe0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTest.kt @@ -2,6 +2,8 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -24,4 +26,24 @@ internal class TranscriptionTest { Transcription.Logprob.builder().token("token").addByte(0.0).logprob(0.0).build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transcription = + Transcription.builder() + .text("text") + .addLogprob( + Transcription.Logprob.builder().token("token").addByte(0.0).logprob(0.0).build() + ) + .build() + + val roundtrippedTranscription = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcription), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscription).isEqualTo(transcription) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEventTest.kt index 51d53953e..f83379bc4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDeltaEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,28 @@ internal class TranscriptionTextDeltaEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transcriptionTextDeltaEvent = + TranscriptionTextDeltaEvent.builder() + .delta("delta") + .addLogprob( + TranscriptionTextDeltaEvent.Logprob.builder() + .token("token") + .addByte(JsonValue.from(mapOf())) + .logprob(0.0) + .build() + ) + .build() + + val roundtrippedTranscriptionTextDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionTextDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionTextDeltaEvent).isEqualTo(transcriptionTextDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEventTest.kt index fa75bd6fd..c2197cff9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionTextDoneEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,28 @@ internal class TranscriptionTextDoneEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transcriptionTextDoneEvent = + TranscriptionTextDoneEvent.builder() + .text("text") + .addLogprob( + TranscriptionTextDoneEvent.Logprob.builder() + .token("token") + .addByte(JsonValue.from(mapOf())) + .logprob(0.0) + .build() + ) + .build() + + val roundtrippedTranscriptionTextDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionTextDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionTextDoneEvent).isEqualTo(transcriptionTextDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt index 98ddd5210..6c3ceef9d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt @@ -2,6 +2,8 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -53,4 +55,38 @@ internal class TranscriptionVerboseTest { assertThat(transcriptionVerbose.words().getOrNull()) .containsExactly(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transcriptionVerbose = + TranscriptionVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .build() + + val roundtrippedTranscriptionVerbose = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionVerbose), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionVerbose).isEqualTo(transcriptionVerbose) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt index 1b139f6fd..0bf1311aa 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt @@ -2,6 +2,8 @@ package com.openai.models.audio.transcriptions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,18 @@ internal class TranscriptionWordTest { assertThat(transcriptionWord.start()).isEqualTo(0.0) assertThat(transcriptionWord.word()).isEqualTo("word") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transcriptionWord = TranscriptionWord.builder().end(0.0).start(0.0).word("word").build() + + val roundtrippedTranscriptionWord = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transcriptionWord), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranscriptionWord).isEqualTo(transcriptionWord) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt index a5b1eef8a..53fc2b4f1 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt @@ -2,8 +2,16 @@ package com.openai.models.audio.translations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.audio.transcriptions.TranscriptionSegment import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class TranslationCreateResponseTest { @@ -17,14 +25,100 @@ internal class TranslationCreateResponseTest { assertThat(translationCreateResponse.verbose()).isEmpty } + @Test + fun ofTranslationRoundtrip() { + val jsonMapper = jsonMapper() + val translationCreateResponse = + TranslationCreateResponse.ofTranslation(Translation.builder().text("text").build()) + + val roundtrippedTranslationCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(translationCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranslationCreateResponse).isEqualTo(translationCreateResponse) + } + @Test fun ofVerbose() { val verbose = - TranslationVerbose.builder().duration(0.0).language("language").text("text").build() + TranslationVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .build() val translationCreateResponse = TranslationCreateResponse.ofVerbose(verbose) assertThat(translationCreateResponse.translation()).isEmpty assertThat(translationCreateResponse.verbose()).contains(verbose) } + + @Test + fun ofVerboseRoundtrip() { + val jsonMapper = jsonMapper() + val translationCreateResponse = + TranslationCreateResponse.ofVerbose( + TranslationVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .build() + ) + + val roundtrippedTranslationCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(translationCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranslationCreateResponse).isEqualTo(translationCreateResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val translationCreateResponse = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { translationCreateResponse.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationTest.kt index 4a8656583..030930f6f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.audio.translations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class TranslationTest { assertThat(translation.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val translation = Translation.builder().text("text").build() + + val roundtrippedTranslation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(translation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranslation).isEqualTo(translation) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt index c77414b83..fc5baf5d4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt @@ -2,6 +2,8 @@ package com.openai.models.audio.translations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.audio.transcriptions.TranscriptionSegment import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat @@ -51,4 +53,37 @@ internal class TranslationVerboseTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val translationVerbose = + TranslationVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .build() + + val roundtrippedTranslationVerbose = + jsonMapper.readValue( + jsonMapper.writeValueAsString(translationVerbose), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTranslationVerbose).isEqualTo(translationVerbose) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchErrorTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchErrorTest.kt index 2ff748693..bc34ad5ef 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchErrorTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchErrorTest.kt @@ -2,6 +2,8 @@ package com.openai.models.batches +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -17,4 +19,19 @@ internal class BatchErrorTest { assertThat(batchError.message()).contains("message") assertThat(batchError.param()).contains("param") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val batchError = + BatchError.builder().code("code").line(0L).message("message").param("param").build() + + val roundtrippedBatchError = + jsonMapper.readValue( + jsonMapper.writeValueAsString(batchError), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBatchError).isEqualTo(batchError) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchRequestCountsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchRequestCountsTest.kt index 3ef292dc2..78ec8b3f3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchRequestCountsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchRequestCountsTest.kt @@ -2,6 +2,8 @@ package com.openai.models.batches +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,19 @@ internal class BatchRequestCountsTest { assertThat(batchRequestCounts.failed()).isEqualTo(0L) assertThat(batchRequestCounts.total()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val batchRequestCounts = + BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() + + val roundtrippedBatchRequestCounts = + jsonMapper.readValue( + jsonMapper.writeValueAsString(batchRequestCounts), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBatchRequestCounts).isEqualTo(batchRequestCounts) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt index 31d4be6d0..985749950 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt @@ -2,7 +2,9 @@ package com.openai.models.batches +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -90,4 +92,54 @@ internal class BatchTest { assertThat(batch.requestCounts()) .contains(BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val batch = + Batch.builder() + .id("id") + .completionWindow("completion_window") + .createdAt(0L) + .endpoint("endpoint") + .inputFileId("input_file_id") + .status(Batch.Status.VALIDATING) + .cancelledAt(0L) + .cancellingAt(0L) + .completedAt(0L) + .errorFileId("error_file_id") + .errors( + Batch.Errors.builder() + .addData( + BatchError.builder() + .code("code") + .line(0L) + .message("message") + .param("param") + .build() + ) + .object_("object") + .build() + ) + .expiredAt(0L) + .expiresAt(0L) + .failedAt(0L) + .finalizingAt(0L) + .inProgressAt(0L) + .metadata( + Batch.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .outputFileId("output_file_id") + .requestCounts( + BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() + ) + .build() + + val roundtrippedBatch = + jsonMapper.readValue(jsonMapper.writeValueAsString(batch), jacksonTypeRef()) + + assertThat(roundtrippedBatch).isEqualTo(batch) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantDeletedTest.kt index 757e7519b..27dc7ea3b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class AssistantDeletedTest { assertThat(assistantDeleted.id()).isEqualTo("id") assertThat(assistantDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val assistantDeleted = AssistantDeleted.builder().id("id").deleted(true).build() + + val roundtrippedAssistantDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantDeleted).isEqualTo(assistantDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantStreamEventTest.kt index a1e1c5331..29bb10c9f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantStreamEventTest.kt @@ -2,11 +2,16 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.ErrorObject import com.openai.models.beta.threads.AssistantToolChoiceOption import com.openai.models.beta.threads.Thread import com.openai.models.beta.threads.messages.ImageFile +import com.openai.models.beta.threads.messages.ImageFileDelta +import com.openai.models.beta.threads.messages.ImageFileDeltaBlock import com.openai.models.beta.threads.messages.Message import com.openai.models.beta.threads.messages.MessageDelta import com.openai.models.beta.threads.messages.MessageDeltaEvent @@ -17,8 +22,12 @@ import com.openai.models.beta.threads.runs.steps.MessageCreationStepDetails import com.openai.models.beta.threads.runs.steps.RunStep import com.openai.models.beta.threads.runs.steps.RunStepDelta import com.openai.models.beta.threads.runs.steps.RunStepDeltaEvent +import com.openai.models.beta.threads.runs.steps.RunStepDeltaMessageDelta import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class AssistantStreamEventTest { @@ -35,9 +44,23 @@ internal class AssistantStreamEventTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .toolResources(Thread.ToolResources.builder().build()) + .toolResources( + Thread.ToolResources.builder() + .codeInterpreter( + Thread.ToolResources.CodeInterpreter.builder() + .addFileId("string") + .build() + ) + .fileSearch( + Thread.ToolResources.FileSearch.builder() + .addVectorStoreId("string") + .build() + ) + .build() + ) .build() ) + .enabled(true) .build() val assistantStreamEvent = AssistantStreamEvent.ofThreadCreated(threadCreated) @@ -68,6 +91,50 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadCreated( + AssistantStreamEvent.ThreadCreated.builder() + .data( + Thread.builder() + .id("id") + .createdAt(0L) + .metadata( + Thread.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .toolResources( + Thread.ToolResources.builder() + .codeInterpreter( + Thread.ToolResources.CodeInterpreter.builder() + .addFileId("string") + .build() + ) + .fileSearch( + Thread.ToolResources.FileSearch.builder() + .addVectorStoreId("string") + .build() + ) + .build() + ) + .build() + ) + .enabled(true) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunCreated() { val threadRunCreated = @@ -81,7 +148,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -127,6 +198,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -136,6 +208,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -168,6 +242,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunCreated( + AssistantStreamEvent.ThreadRunCreated.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunQueued() { val threadRunQueued = @@ -181,7 +346,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -227,6 +396,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -236,6 +406,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -268,6 +440,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunQueuedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunQueued( + AssistantStreamEvent.ThreadRunQueued.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunInProgress() { val threadRunInProgress = @@ -281,7 +544,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -327,6 +594,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -336,6 +604,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -368,6 +638,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunInProgress( + AssistantStreamEvent.ThreadRunInProgress.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunRequiresAction() { val threadRunRequiresAction = @@ -381,7 +742,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -427,6 +792,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -436,6 +802,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -469,6 +837,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunRequiresActionRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunRequiresAction( + AssistantStreamEvent.ThreadRunRequiresAction.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunCompleted() { val threadRunCompleted = @@ -482,7 +941,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -528,6 +991,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -537,6 +1001,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -569,6 +1035,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunCompleted( + AssistantStreamEvent.ThreadRunCompleted.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunIncomplete() { val threadRunIncomplete = @@ -582,7 +1139,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -628,6 +1189,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -637,6 +1199,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -669,6 +1233,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunIncompleteRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunIncomplete( + AssistantStreamEvent.ThreadRunIncomplete.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunFailed() { val threadRunFailed = @@ -682,7 +1337,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -728,6 +1387,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -737,6 +1397,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -769,6 +1431,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunFailedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunFailed( + AssistantStreamEvent.ThreadRunFailed.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunCancelling() { val threadRunCancelling = @@ -782,7 +1535,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -828,6 +1585,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -837,6 +1595,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -869,6 +1629,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunCancellingRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunCancelling( + AssistantStreamEvent.ThreadRunCancelling.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunCancelled() { val threadRunCancelled = @@ -882,7 +1733,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -928,6 +1783,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -937,6 +1793,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -969,6 +1827,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunCancelledRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunCancelled( + AssistantStreamEvent.ThreadRunCancelled.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunExpired() { val threadRunExpired = @@ -982,7 +1931,11 @@ internal class AssistantStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -1028,6 +1981,7 @@ internal class AssistantStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -1037,6 +1991,8 @@ internal class AssistantStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -1069,6 +2025,97 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunExpiredRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunExpired( + AssistantStreamEvent.ThreadRunExpired.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepCreated() { val threadRunStepCreated = @@ -1141,6 +2188,62 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepCreated( + AssistantStreamEvent.ThreadRunStepCreated.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepInProgress() { val threadRunStepInProgress = @@ -1214,6 +2317,62 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepInProgress( + AssistantStreamEvent.ThreadRunStepInProgress.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepDelta() { val threadRunStepDelta = @@ -1221,7 +2380,19 @@ internal class AssistantStreamEventTest { .data( RunStepDeltaEvent.builder() .id("id") - .delta(RunStepDelta.builder().build()) + .delta( + RunStepDelta.builder() + .stepDetails( + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + ) + .build() + ) .build() ) .build() @@ -1254,6 +2425,42 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepDelta( + AssistantStreamEvent.ThreadRunStepDelta.builder() + .data( + RunStepDeltaEvent.builder() + .id("id") + .delta( + RunStepDelta.builder() + .stepDetails( + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepCompleted() { val threadRunStepCompleted = @@ -1327,6 +2534,62 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepCompleted( + AssistantStreamEvent.ThreadRunStepCompleted.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepFailed() { val threadRunStepFailed = @@ -1399,6 +2662,62 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepFailedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepFailed( + AssistantStreamEvent.ThreadRunStepFailed.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepCancelled() { val threadRunStepCancelled = @@ -1472,6 +2791,62 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepCancelledRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepCancelled( + AssistantStreamEvent.ThreadRunStepCancelled.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadRunStepExpired() { val threadRunStepExpired = @@ -1544,6 +2919,62 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadRunStepExpiredRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadRunStepExpired( + AssistantStreamEvent.ThreadRunStepExpired.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadMessageCreated() { val threadMessageCreated = @@ -1552,9 +2983,19 @@ internal class AssistantStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -1603,6 +3044,59 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadMessageCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadMessageCreated( + AssistantStreamEvent.ThreadMessageCreated.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadMessageInProgress() { val threadMessageInProgress = @@ -1611,9 +3105,19 @@ internal class AssistantStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -1663,6 +3167,59 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadMessageInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadMessageInProgress( + AssistantStreamEvent.ThreadMessageInProgress.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadMessageDelta() { val threadMessageDelta = @@ -1670,7 +3227,22 @@ internal class AssistantStreamEventTest { .data( MessageDeltaEvent.builder() .id("id") - .delta(MessageDelta.builder().build()) + .delta( + MessageDelta.builder() + .addContent( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + .role(MessageDelta.Role.USER) + .build() + ) .build() ) .build() @@ -1703,6 +3275,45 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadMessageDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadMessageDelta( + AssistantStreamEvent.ThreadMessageDelta.builder() + .data( + MessageDeltaEvent.builder() + .id("id") + .delta( + MessageDelta.builder() + .addContent( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + .role(MessageDelta.Role.USER) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadMessageCompleted() { val threadMessageCompleted = @@ -1711,9 +3322,19 @@ internal class AssistantStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -1763,6 +3384,59 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadMessageCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadMessageCompleted( + AssistantStreamEvent.ThreadMessageCompleted.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofThreadMessageIncomplete() { val threadMessageIncomplete = @@ -1771,9 +3445,19 @@ internal class AssistantStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -1823,6 +3507,59 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.errorEvent()).isEmpty } + @Test + fun ofThreadMessageIncompleteRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofThreadMessageIncomplete( + AssistantStreamEvent.ThreadMessageIncomplete.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + @Test fun ofErrorEvent() { val errorEvent = @@ -1864,4 +3601,48 @@ internal class AssistantStreamEventTest { assertThat(assistantStreamEvent.threadMessageIncomplete()).isEmpty assertThat(assistantStreamEvent.errorEvent()).contains(errorEvent) } + + @Test + fun ofErrorEventRoundtrip() { + val jsonMapper = jsonMapper() + val assistantStreamEvent = + AssistantStreamEvent.ofErrorEvent( + AssistantStreamEvent.ErrorEvent.builder() + .data( + ErrorObject.builder() + .code("code") + .message("message") + .param("param") + .type("type") + .build() + ) + .build() + ) + + val roundtrippedAssistantStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantStreamEvent).isEqualTo(assistantStreamEvent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val assistantStreamEvent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { assistantStreamEvent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantTest.kt index adfad5e46..9c1852965 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.beta.threads.AssistantResponseFormatOption import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -77,4 +79,49 @@ internal class AssistantTest { ) assertThat(assistant.topP()).contains(1.0) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val assistant = + Assistant.builder() + .id("id") + .createdAt(0L) + .description("description") + .instructions("instructions") + .metadata( + Assistant.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .name("name") + .addTool(CodeInterpreterTool.builder().build()) + .responseFormatAuto() + .temperature(1.0) + .toolResources( + Assistant.ToolResources.builder() + .codeInterpreter( + Assistant.ToolResources.CodeInterpreter.builder() + .addFileId("string") + .build() + ) + .fileSearch( + Assistant.ToolResources.FileSearch.builder() + .addVectorStoreId("string") + .build() + ) + .build() + ) + .topP(1.0) + .build() + + val roundtrippedAssistant = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistant), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistant).isEqualTo(assistant) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantToolTest.kt index f8c3c52ff..5a6bedfea 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/AssistantToolTest.kt @@ -2,9 +2,17 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.FunctionDefinition +import com.openai.models.FunctionParameters import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class AssistantToolTest { @@ -19,9 +27,36 @@ internal class AssistantToolTest { assertThat(assistantTool.function()).isEmpty } + @Test + fun ofCodeInterpreterRoundtrip() { + val jsonMapper = jsonMapper() + val assistantTool = AssistantTool.ofCodeInterpreter(CodeInterpreterTool.builder().build()) + + val roundtrippedAssistantTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantTool).isEqualTo(assistantTool) + } + @Test fun ofFileSearch() { - val fileSearch = FileSearchTool.builder().build() + val fileSearch = + FileSearchTool.builder() + .fileSearch( + FileSearchTool.FileSearch.builder() + .maxNumResults(1L) + .rankingOptions( + FileSearchTool.FileSearch.RankingOptions.builder() + .scoreThreshold(0.0) + .ranker(FileSearchTool.FileSearch.RankingOptions.Ranker.AUTO) + .build() + ) + .build() + ) + .build() val assistantTool = AssistantTool.ofFileSearch(fileSearch) @@ -30,11 +65,51 @@ internal class AssistantToolTest { assertThat(assistantTool.function()).isEmpty } + @Test + fun ofFileSearchRoundtrip() { + val jsonMapper = jsonMapper() + val assistantTool = + AssistantTool.ofFileSearch( + FileSearchTool.builder() + .fileSearch( + FileSearchTool.FileSearch.builder() + .maxNumResults(1L) + .rankingOptions( + FileSearchTool.FileSearch.RankingOptions.builder() + .scoreThreshold(0.0) + .ranker(FileSearchTool.FileSearch.RankingOptions.Ranker.AUTO) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedAssistantTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantTool).isEqualTo(assistantTool) + } + @Test fun ofFunction() { val function = FunctionTool.builder() - .function(FunctionDefinition.builder().name("name").build()) + .function( + FunctionDefinition.builder() + .name("name") + .description("description") + .parameters( + FunctionParameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) .build() val assistantTool = AssistantTool.ofFunction(function) @@ -43,4 +118,52 @@ internal class AssistantToolTest { assertThat(assistantTool.fileSearch()).isEmpty assertThat(assistantTool.function()).contains(function) } + + @Test + fun ofFunctionRoundtrip() { + val jsonMapper = jsonMapper() + val assistantTool = + AssistantTool.ofFunction( + FunctionTool.builder() + .function( + FunctionDefinition.builder() + .name("name") + .description("description") + .parameters( + FunctionParameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) + .build() + ) + + val roundtrippedAssistantTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantTool).isEqualTo(assistantTool) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val assistantTool = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { assistantTool.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/CodeInterpreterToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/CodeInterpreterToolTest.kt index 241a509fc..18ce77a97 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/CodeInterpreterToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/CodeInterpreterToolTest.kt @@ -2,6 +2,9 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class CodeInterpreterToolTest { @@ -10,4 +13,18 @@ internal class CodeInterpreterToolTest { fun create() { val codeInterpreterTool = CodeInterpreterTool.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val codeInterpreterTool = CodeInterpreterTool.builder().build() + + val roundtrippedCodeInterpreterTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(codeInterpreterTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCodeInterpreterTool).isEqualTo(codeInterpreterTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FileSearchToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FileSearchToolTest.kt index 40b0a05a8..01c71c22b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FileSearchToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FileSearchToolTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -37,4 +39,31 @@ internal class FileSearchToolTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileSearchTool = + FileSearchTool.builder() + .fileSearch( + FileSearchTool.FileSearch.builder() + .maxNumResults(1L) + .rankingOptions( + FileSearchTool.FileSearch.RankingOptions.builder() + .scoreThreshold(0.0) + .ranker(FileSearchTool.FileSearch.RankingOptions.Ranker.AUTO) + .build() + ) + .build() + ) + .build() + + val roundtrippedFileSearchTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileSearchTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileSearchTool).isEqualTo(fileSearchTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FunctionToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FunctionToolTest.kt index 31ad23483..d9c90280f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FunctionToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/FunctionToolTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.FunctionDefinition import com.openai.models.FunctionParameters import org.assertj.core.api.Assertions.assertThat @@ -42,4 +44,32 @@ internal class FunctionToolTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val functionTool = + FunctionTool.builder() + .function( + FunctionDefinition.builder() + .name("name") + .description("description") + .parameters( + FunctionParameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) + .build() + + val roundtrippedFunctionTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(functionTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFunctionTool).isEqualTo(functionTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/MessageStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/MessageStreamEventTest.kt index daf9aa436..811820f88 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/MessageStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/MessageStreamEventTest.kt @@ -2,13 +2,21 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.beta.threads.messages.ImageFile +import com.openai.models.beta.threads.messages.ImageFileDelta +import com.openai.models.beta.threads.messages.ImageFileDeltaBlock import com.openai.models.beta.threads.messages.Message import com.openai.models.beta.threads.messages.MessageDelta import com.openai.models.beta.threads.messages.MessageDeltaEvent import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class MessageStreamEventTest { @@ -20,9 +28,19 @@ internal class MessageStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -52,6 +70,59 @@ internal class MessageStreamEventTest { assertThat(messageStreamEvent.threadMessageIncomplete()).isEmpty } + @Test + fun ofThreadMessageCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val messageStreamEvent = + MessageStreamEvent.ofThreadMessageCreated( + MessageStreamEvent.ThreadMessageCreated.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedMessageStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageStreamEvent).isEqualTo(messageStreamEvent) + } + @Test fun ofThreadMessageInProgress() { val threadMessageInProgress = @@ -60,9 +131,19 @@ internal class MessageStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -93,6 +174,59 @@ internal class MessageStreamEventTest { assertThat(messageStreamEvent.threadMessageIncomplete()).isEmpty } + @Test + fun ofThreadMessageInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val messageStreamEvent = + MessageStreamEvent.ofThreadMessageInProgress( + MessageStreamEvent.ThreadMessageInProgress.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedMessageStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageStreamEvent).isEqualTo(messageStreamEvent) + } + @Test fun ofThreadMessageDelta() { val threadMessageDelta = @@ -100,7 +234,22 @@ internal class MessageStreamEventTest { .data( MessageDeltaEvent.builder() .id("id") - .delta(MessageDelta.builder().build()) + .delta( + MessageDelta.builder() + .addContent( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + .role(MessageDelta.Role.USER) + .build() + ) .build() ) .build() @@ -114,6 +263,45 @@ internal class MessageStreamEventTest { assertThat(messageStreamEvent.threadMessageIncomplete()).isEmpty } + @Test + fun ofThreadMessageDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val messageStreamEvent = + MessageStreamEvent.ofThreadMessageDelta( + MessageStreamEvent.ThreadMessageDelta.builder() + .data( + MessageDeltaEvent.builder() + .id("id") + .delta( + MessageDelta.builder() + .addContent( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + .role(MessageDelta.Role.USER) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedMessageStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageStreamEvent).isEqualTo(messageStreamEvent) + } + @Test fun ofThreadMessageCompleted() { val threadMessageCompleted = @@ -122,9 +310,19 @@ internal class MessageStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -154,6 +352,59 @@ internal class MessageStreamEventTest { assertThat(messageStreamEvent.threadMessageIncomplete()).isEmpty } + @Test + fun ofThreadMessageCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val messageStreamEvent = + MessageStreamEvent.ofThreadMessageCompleted( + MessageStreamEvent.ThreadMessageCompleted.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedMessageStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageStreamEvent).isEqualTo(messageStreamEvent) + } + @Test fun ofThreadMessageIncomplete() { val threadMessageIncomplete = @@ -162,9 +413,19 @@ internal class MessageStreamEventTest { Message.builder() .id("id") .assistantId("assistant_id") - .addAttachment(Message.Attachment.builder().build()) + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) .completedAt(0L) - .addImageFileContent(ImageFile.builder().fileId("file_id").build()) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) .createdAt(0L) .incompleteAt(0L) .incompleteDetails( @@ -194,4 +455,75 @@ internal class MessageStreamEventTest { assertThat(messageStreamEvent.threadMessageCompleted()).isEmpty assertThat(messageStreamEvent.threadMessageIncomplete()).contains(threadMessageIncomplete) } + + @Test + fun ofThreadMessageIncompleteRoundtrip() { + val jsonMapper = jsonMapper() + val messageStreamEvent = + MessageStreamEvent.ofThreadMessageIncomplete( + MessageStreamEvent.ThreadMessageIncomplete.builder() + .data( + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder() + .fileId("file_id") + .detail(ImageFile.Detail.AUTO) + .build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + ) + .build() + ) + + val roundtrippedMessageStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageStreamEvent).isEqualTo(messageStreamEvent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val messageStreamEvent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { messageStreamEvent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStepStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStepStreamEventTest.kt index e99a638d2..a35ab365b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStepStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStepStreamEventTest.kt @@ -2,13 +2,20 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.beta.threads.runs.steps.MessageCreationStepDetails import com.openai.models.beta.threads.runs.steps.RunStep import com.openai.models.beta.threads.runs.steps.RunStepDelta import com.openai.models.beta.threads.runs.steps.RunStepDeltaEvent +import com.openai.models.beta.threads.runs.steps.RunStepDeltaMessageDelta import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class RunStepStreamEventTest { @@ -67,6 +74,62 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepExpired()).isEmpty } + @Test + fun ofThreadRunStepCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepCreated( + RunStepStreamEvent.ThreadRunStepCreated.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + @Test fun ofThreadRunStepInProgress() { val threadRunStepInProgress = @@ -123,6 +186,62 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepExpired()).isEmpty } + @Test + fun ofThreadRunStepInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepInProgress( + RunStepStreamEvent.ThreadRunStepInProgress.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + @Test fun ofThreadRunStepDelta() { val threadRunStepDelta = @@ -130,7 +249,19 @@ internal class RunStepStreamEventTest { .data( RunStepDeltaEvent.builder() .id("id") - .delta(RunStepDelta.builder().build()) + .delta( + RunStepDelta.builder() + .stepDetails( + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + ) + .build() + ) .build() ) .build() @@ -146,6 +277,42 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepExpired()).isEmpty } + @Test + fun ofThreadRunStepDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepDelta( + RunStepStreamEvent.ThreadRunStepDelta.builder() + .data( + RunStepDeltaEvent.builder() + .id("id") + .delta( + RunStepDelta.builder() + .stepDetails( + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + @Test fun ofThreadRunStepCompleted() { val threadRunStepCompleted = @@ -201,6 +368,62 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepExpired()).isEmpty } + @Test + fun ofThreadRunStepCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepCompleted( + RunStepStreamEvent.ThreadRunStepCompleted.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + @Test fun ofThreadRunStepFailed() { val threadRunStepFailed = @@ -256,6 +479,62 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepExpired()).isEmpty } + @Test + fun ofThreadRunStepFailedRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepFailed( + RunStepStreamEvent.ThreadRunStepFailed.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + @Test fun ofThreadRunStepCancelled() { val threadRunStepCancelled = @@ -311,6 +590,62 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepExpired()).isEmpty } + @Test + fun ofThreadRunStepCancelledRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepCancelled( + RunStepStreamEvent.ThreadRunStepCancelled.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + @Test fun ofThreadRunStepExpired() { val threadRunStepExpired = @@ -365,4 +700,78 @@ internal class RunStepStreamEventTest { assertThat(runStepStreamEvent.threadRunStepCancelled()).isEmpty assertThat(runStepStreamEvent.threadRunStepExpired()).contains(threadRunStepExpired) } + + @Test + fun ofThreadRunStepExpiredRoundtrip() { + val jsonMapper = jsonMapper() + val runStepStreamEvent = + RunStepStreamEvent.ofThreadRunStepExpired( + RunStepStreamEvent.ThreadRunStepExpired.builder() + .data( + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedRunStepStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepStreamEvent).isEqualTo(runStepStreamEvent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val runStepStreamEvent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { runStepStreamEvent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStreamEventTest.kt index d6d7e9651..eff518063 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/RunStreamEventTest.kt @@ -2,13 +2,19 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.beta.threads.AssistantToolChoiceOption import com.openai.models.beta.threads.runs.RequiredActionFunctionToolCall import com.openai.models.beta.threads.runs.Run import com.openai.models.beta.threads.runs.RunStatus import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class RunStreamEventTest { @@ -25,7 +31,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -71,6 +81,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -80,6 +91,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -98,6 +111,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunCreated( + RunStreamEvent.ThreadRunCreated.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunQueued() { val threadRunQueued = @@ -111,7 +215,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -157,6 +265,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -166,6 +275,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -184,6 +295,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunQueuedRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunQueued( + RunStreamEvent.ThreadRunQueued.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunInProgress() { val threadRunInProgress = @@ -197,7 +399,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -243,6 +449,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -252,6 +459,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -270,6 +479,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunInProgress( + RunStreamEvent.ThreadRunInProgress.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunRequiresAction() { val threadRunRequiresAction = @@ -283,7 +583,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -329,6 +633,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -338,6 +643,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -356,6 +663,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunRequiresActionRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunRequiresAction( + RunStreamEvent.ThreadRunRequiresAction.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunCompleted() { val threadRunCompleted = @@ -369,7 +767,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -415,6 +817,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -424,6 +827,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -442,6 +847,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunCompleted( + RunStreamEvent.ThreadRunCompleted.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunIncomplete() { val threadRunIncomplete = @@ -455,7 +951,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -501,6 +1001,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -510,6 +1011,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -528,6 +1031,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunIncompleteRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunIncomplete( + RunStreamEvent.ThreadRunIncomplete.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunFailed() { val threadRunFailed = @@ -541,7 +1135,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -587,6 +1185,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -596,6 +1195,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -614,6 +1215,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunFailedRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunFailed( + RunStreamEvent.ThreadRunFailed.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunCancelling() { val threadRunCancelling = @@ -627,7 +1319,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -673,6 +1369,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -682,6 +1379,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -700,6 +1399,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunCancellingRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunCancelling( + RunStreamEvent.ThreadRunCancelling.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunCancelled() { val threadRunCancelled = @@ -713,7 +1503,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -759,6 +1553,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -768,6 +1563,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -786,6 +1583,97 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunExpired()).isEmpty } + @Test + fun ofThreadRunCancelledRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunCancelled( + RunStreamEvent.ThreadRunCancelled.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + @Test fun ofThreadRunExpired() { val threadRunExpired = @@ -799,7 +1687,11 @@ internal class RunStreamEventTest { .createdAt(0L) .expiresAt(0L) .failedAt(0L) - .incompleteDetails(Run.IncompleteDetails.builder().build()) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) .instructions("instructions") .lastError( Run.LastError.builder() @@ -845,6 +1737,7 @@ internal class RunStreamEventTest { .truncationStrategy( Run.TruncationStrategy.builder() .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) .build() ) .usage( @@ -854,6 +1747,8 @@ internal class RunStreamEventTest { .totalTokens(0L) .build() ) + .temperature(0.0) + .topP(0.0) .build() ) .build() @@ -871,4 +1766,113 @@ internal class RunStreamEventTest { assertThat(runStreamEvent.threadRunCancelled()).isEmpty assertThat(runStreamEvent.threadRunExpired()).contains(threadRunExpired) } + + @Test + fun ofThreadRunExpiredRoundtrip() { + val jsonMapper = jsonMapper() + val runStreamEvent = + RunStreamEvent.ofThreadRunExpired( + RunStreamEvent.ThreadRunExpired.builder() + .data( + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + ) + .build() + ) + + val roundtrippedRunStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStreamEvent).isEqualTo(runStreamEvent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val runStreamEvent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { runStreamEvent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/ThreadStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/ThreadStreamEventTest.kt index af1002551..e41100911 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/ThreadStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/assistants/ThreadStreamEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.assistants +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.beta.threads.Thread import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -69,4 +71,46 @@ internal class ThreadStreamEventTest { ) assertThat(threadStreamEvent.enabled()).contains(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val threadStreamEvent = + ThreadStreamEvent.builder() + .data( + Thread.builder() + .id("id") + .createdAt(0L) + .metadata( + Thread.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .toolResources( + Thread.ToolResources.builder() + .codeInterpreter( + Thread.ToolResources.CodeInterpreter.builder() + .addFileId("string") + .build() + ) + .fileSearch( + Thread.ToolResources.FileSearch.builder() + .addVectorStoreId("string") + .build() + ) + .build() + ) + .build() + ) + .enabled(true) + .build() + + val roundtrippedThreadStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(threadStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedThreadStreamEvent).isEqualTo(threadStreamEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOptionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOptionTest.kt index 666790a41..468a4e1db 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOptionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantResponseFormatOptionTest.kt @@ -2,12 +2,16 @@ package com.openai.models.beta.threads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatJsonSchema import com.openai.models.ResponseFormatText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows internal class AssistantResponseFormatOptionTest { @@ -21,6 +25,21 @@ internal class AssistantResponseFormatOptionTest { assertThat(assistantResponseFormatOption.responseFormatJsonSchema()).isEmpty } + @Test + fun ofAutoRoundtrip() { + val jsonMapper = jsonMapper() + val assistantResponseFormatOption = AssistantResponseFormatOption.ofAuto() + + val roundtrippedAssistantResponseFormatOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantResponseFormatOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantResponseFormatOption) + .isEqualTo(assistantResponseFormatOption) + } + @Test fun ofResponseFormatText() { val responseFormatText = ResponseFormatText.builder().build() @@ -34,6 +53,22 @@ internal class AssistantResponseFormatOptionTest { assertThat(assistantResponseFormatOption.responseFormatJsonSchema()).isEmpty } + @Test + fun ofResponseFormatTextRoundtrip() { + val jsonMapper = jsonMapper() + val assistantResponseFormatOption = + AssistantResponseFormatOption.ofResponseFormatText(ResponseFormatText.builder().build()) + + val roundtrippedAssistantResponseFormatOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantResponseFormatOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantResponseFormatOption) + .isEqualTo(assistantResponseFormatOption) + } + @Test fun ofResponseFormatJsonObject() { val responseFormatJsonObject = ResponseFormatJsonObject.builder().build() @@ -48,11 +83,40 @@ internal class AssistantResponseFormatOptionTest { assertThat(assistantResponseFormatOption.responseFormatJsonSchema()).isEmpty } + @Test + fun ofResponseFormatJsonObjectRoundtrip() { + val jsonMapper = jsonMapper() + val assistantResponseFormatOption = + AssistantResponseFormatOption.ofResponseFormatJsonObject( + ResponseFormatJsonObject.builder().build() + ) + + val roundtrippedAssistantResponseFormatOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantResponseFormatOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantResponseFormatOption) + .isEqualTo(assistantResponseFormatOption) + } + @Test fun ofResponseFormatJsonSchema() { val responseFormatJsonSchema = ResponseFormatJsonSchema.builder() - .jsonSchema(ResponseFormatJsonSchema.JsonSchema.builder().name("name").build()) + .jsonSchema( + ResponseFormatJsonSchema.JsonSchema.builder() + .name("name") + .description("description") + .schema( + ResponseFormatJsonSchema.JsonSchema.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) .build() val assistantResponseFormatOption = @@ -64,4 +128,46 @@ internal class AssistantResponseFormatOptionTest { assertThat(assistantResponseFormatOption.responseFormatJsonSchema()) .contains(responseFormatJsonSchema) } + + @Test + fun ofResponseFormatJsonSchemaRoundtrip() { + val jsonMapper = jsonMapper() + val assistantResponseFormatOption = + AssistantResponseFormatOption.ofResponseFormatJsonSchema( + ResponseFormatJsonSchema.builder() + .jsonSchema( + ResponseFormatJsonSchema.JsonSchema.builder() + .name("name") + .description("description") + .schema( + ResponseFormatJsonSchema.JsonSchema.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) + .build() + ) + + val roundtrippedAssistantResponseFormatOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantResponseFormatOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantResponseFormatOption) + .isEqualTo(assistantResponseFormatOption) + } + + @Test + fun incompatibleJsonShapeDeserializesToUnknown() { + val value = JsonValue.from(listOf("invalid", "array")) + val assistantResponseFormatOption = + jsonMapper().convertValue(value, jacksonTypeRef()) + + val e = + assertThrows { assistantResponseFormatOption.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunctionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunctionTest.kt index 62420c3bc..48d8e616e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunctionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceFunctionTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class AssistantToolChoiceFunctionTest { assertThat(assistantToolChoiceFunction.name()).isEqualTo("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val assistantToolChoiceFunction = AssistantToolChoiceFunction.builder().name("name").build() + + val roundtrippedAssistantToolChoiceFunction = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantToolChoiceFunction), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantToolChoiceFunction).isEqualTo(assistantToolChoiceFunction) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOptionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOptionTest.kt index cb0c8de7d..077a559fc 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOptionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceOptionTest.kt @@ -2,8 +2,13 @@ package com.openai.models.beta.threads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows internal class AssistantToolChoiceOptionTest { @@ -17,10 +22,28 @@ internal class AssistantToolChoiceOptionTest { assertThat(assistantToolChoiceOption.assistantToolChoice()).isEmpty } + @Test + fun ofAutoRoundtrip() { + val jsonMapper = jsonMapper() + val assistantToolChoiceOption = + AssistantToolChoiceOption.ofAuto(AssistantToolChoiceOption.Auto.NONE) + + val roundtrippedAssistantToolChoiceOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantToolChoiceOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantToolChoiceOption).isEqualTo(assistantToolChoiceOption) + } + @Test fun ofAssistantToolChoice() { val assistantToolChoice = - AssistantToolChoice.builder().type(AssistantToolChoice.Type.FUNCTION).build() + AssistantToolChoice.builder() + .type(AssistantToolChoice.Type.FUNCTION) + .function(AssistantToolChoiceFunction.builder().name("name").build()) + .build() val assistantToolChoiceOption = AssistantToolChoiceOption.ofAssistantToolChoice(assistantToolChoice) @@ -28,4 +51,34 @@ internal class AssistantToolChoiceOptionTest { assertThat(assistantToolChoiceOption.auto()).isEmpty assertThat(assistantToolChoiceOption.assistantToolChoice()).contains(assistantToolChoice) } + + @Test + fun ofAssistantToolChoiceRoundtrip() { + val jsonMapper = jsonMapper() + val assistantToolChoiceOption = + AssistantToolChoiceOption.ofAssistantToolChoice( + AssistantToolChoice.builder() + .type(AssistantToolChoice.Type.FUNCTION) + .function(AssistantToolChoiceFunction.builder().name("name").build()) + .build() + ) + + val roundtrippedAssistantToolChoiceOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantToolChoiceOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantToolChoiceOption).isEqualTo(assistantToolChoiceOption) + } + + @Test + fun incompatibleJsonShapeDeserializesToUnknown() { + val value = JsonValue.from(listOf("invalid", "array")) + val assistantToolChoiceOption = + jsonMapper().convertValue(value, jacksonTypeRef()) + + val e = assertThrows { assistantToolChoiceOption.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceTest.kt index 86d74821a..014d3cac5 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/AssistantToolChoiceTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -19,4 +21,22 @@ internal class AssistantToolChoiceTest { assertThat(assistantToolChoice.function()) .contains(AssistantToolChoiceFunction.builder().name("name").build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val assistantToolChoice = + AssistantToolChoice.builder() + .type(AssistantToolChoice.Type.FUNCTION) + .function(AssistantToolChoiceFunction.builder().name("name").build()) + .build() + + val roundtrippedAssistantToolChoice = + jsonMapper.readValue( + jsonMapper.writeValueAsString(assistantToolChoice), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAssistantToolChoice).isEqualTo(assistantToolChoice) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadDeletedTest.kt index 028f8bd8c..6bc4351c3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class ThreadDeletedTest { assertThat(threadDeleted.id()).isEqualTo("id") assertThat(threadDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val threadDeleted = ThreadDeleted.builder().id("id").deleted(true).build() + + val roundtrippedThreadDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(threadDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedThreadDeleted).isEqualTo(threadDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadTest.kt index cb559d7e6..e98c42b4f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/ThreadTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.threads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -55,4 +57,38 @@ internal class ThreadTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val thread = + Thread.builder() + .id("id") + .createdAt(0L) + .metadata( + Thread.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .toolResources( + Thread.ToolResources.builder() + .codeInterpreter( + Thread.ToolResources.CodeInterpreter.builder() + .addFileId("string") + .build() + ) + .fileSearch( + Thread.ToolResources.FileSearch.builder() + .addVectorStoreId("string") + .build() + ) + .build() + ) + .build() + + val roundtrippedThread = + jsonMapper.readValue(jsonMapper.writeValueAsString(thread), jacksonTypeRef()) + + assertThat(roundtrippedThread).isEqualTo(thread) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationDeltaTest.kt index 9925af7d5..d2723e517 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationDeltaTest.kt @@ -2,14 +2,33 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class AnnotationDeltaTest { @Test fun ofFileCitation() { - val fileCitation = FileCitationDeltaAnnotation.builder().index(0L).build() + val fileCitation = + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() val annotationDelta = AnnotationDelta.ofFileCitation(fileCitation) @@ -17,13 +36,89 @@ internal class AnnotationDeltaTest { assertThat(annotationDelta.filePath()).isEmpty } + @Test + fun ofFileCitationRoundtrip() { + val jsonMapper = jsonMapper() + val annotationDelta = + AnnotationDelta.ofFileCitation( + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + + val roundtrippedAnnotationDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(annotationDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAnnotationDelta).isEqualTo(annotationDelta) + } + @Test fun ofFilePath() { - val filePath = FilePathDeltaAnnotation.builder().index(0L).build() + val filePath = + FilePathDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .filePath(FilePathDeltaAnnotation.FilePath.builder().fileId("file_id").build()) + .startIndex(0L) + .text("text") + .build() val annotationDelta = AnnotationDelta.ofFilePath(filePath) assertThat(annotationDelta.fileCitation()).isEmpty assertThat(annotationDelta.filePath()).contains(filePath) } + + @Test + fun ofFilePathRoundtrip() { + val jsonMapper = jsonMapper() + val annotationDelta = + AnnotationDelta.ofFilePath( + FilePathDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .filePath(FilePathDeltaAnnotation.FilePath.builder().fileId("file_id").build()) + .startIndex(0L) + .text("text") + .build() + ) + + val roundtrippedAnnotationDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(annotationDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAnnotationDelta).isEqualTo(annotationDelta) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val annotationDelta = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { annotationDelta.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationTest.kt index 7480f8f67..370e765dd 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/AnnotationTest.kt @@ -2,8 +2,15 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class AnnotationTest { @@ -25,6 +32,30 @@ internal class AnnotationTest { assertThat(annotation.filePath()).isEmpty } + @Test + fun ofFileCitationRoundtrip() { + val jsonMapper = jsonMapper() + val annotation = + Annotation.ofFileCitation( + FileCitationAnnotation.builder() + .endIndex(0L) + .fileCitation( + FileCitationAnnotation.FileCitation.builder().fileId("file_id").build() + ) + .startIndex(0L) + .text("text") + .build() + ) + + val roundtrippedAnnotation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(annotation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAnnotation).isEqualTo(annotation) + } + @Test fun ofFilePath() { val filePath = @@ -40,4 +71,43 @@ internal class AnnotationTest { assertThat(annotation.fileCitation()).isEmpty assertThat(annotation.filePath()).contains(filePath) } + + @Test + fun ofFilePathRoundtrip() { + val jsonMapper = jsonMapper() + val annotation = + Annotation.ofFilePath( + FilePathAnnotation.builder() + .endIndex(0L) + .filePath(FilePathAnnotation.FilePath.builder().fileId("file_id").build()) + .startIndex(0L) + .text("text") + .build() + ) + + val roundtrippedAnnotation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(annotation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAnnotation).isEqualTo(annotation) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val annotation = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { annotation.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotationTest.kt index 15973f413..6d0507bc2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationAnnotationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -25,4 +27,26 @@ internal class FileCitationAnnotationTest { assertThat(fileCitationAnnotation.startIndex()).isEqualTo(0L) assertThat(fileCitationAnnotation.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileCitationAnnotation = + FileCitationAnnotation.builder() + .endIndex(0L) + .fileCitation( + FileCitationAnnotation.FileCitation.builder().fileId("file_id").build() + ) + .startIndex(0L) + .text("text") + .build() + + val roundtrippedFileCitationAnnotation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileCitationAnnotation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileCitationAnnotation).isEqualTo(fileCitationAnnotation) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotationTest.kt index 41820c4fb..0264f4c62 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FileCitationDeltaAnnotationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -35,4 +37,30 @@ internal class FileCitationDeltaAnnotationTest { assertThat(fileCitationDeltaAnnotation.startIndex()).contains(0L) assertThat(fileCitationDeltaAnnotation.text()).contains("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileCitationDeltaAnnotation = + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() + + val roundtrippedFileCitationDeltaAnnotation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileCitationDeltaAnnotation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileCitationDeltaAnnotation).isEqualTo(fileCitationDeltaAnnotation) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotationTest.kt index ccb85179a..56041ee6b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathAnnotationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -23,4 +25,24 @@ internal class FilePathAnnotationTest { assertThat(filePathAnnotation.startIndex()).isEqualTo(0L) assertThat(filePathAnnotation.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val filePathAnnotation = + FilePathAnnotation.builder() + .endIndex(0L) + .filePath(FilePathAnnotation.FilePath.builder().fileId("file_id").build()) + .startIndex(0L) + .text("text") + .build() + + val roundtrippedFilePathAnnotation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(filePathAnnotation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFilePathAnnotation).isEqualTo(filePathAnnotation) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotationTest.kt index 71ec7b125..bac46bcc3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/FilePathDeltaAnnotationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -25,4 +27,25 @@ internal class FilePathDeltaAnnotationTest { assertThat(filePathDeltaAnnotation.startIndex()).contains(0L) assertThat(filePathDeltaAnnotation.text()).contains("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val filePathDeltaAnnotation = + FilePathDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .filePath(FilePathDeltaAnnotation.FilePath.builder().fileId("file_id").build()) + .startIndex(0L) + .text("text") + .build() + + val roundtrippedFilePathDeltaAnnotation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(filePathDeltaAnnotation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFilePathDeltaAnnotation).isEqualTo(filePathDeltaAnnotation) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlockTest.kt index 061604e41..5be6fe23f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileContentBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -19,4 +21,23 @@ internal class ImageFileContentBlockTest { assertThat(imageFileContentBlock.imageFile()) .isEqualTo(ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageFileContentBlock = + ImageFileContentBlock.builder() + .imageFile( + ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + ) + .build() + + val roundtrippedImageFileContentBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageFileContentBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageFileContentBlock).isEqualTo(imageFileContentBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlockTest.kt index 723aed6bd..95c1ce88c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -29,4 +31,27 @@ internal class ImageFileDeltaBlockTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageFileDeltaBlock = + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + + val roundtrippedImageFileDeltaBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageFileDeltaBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageFileDeltaBlock).isEqualTo(imageFileDeltaBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaTest.kt index 283853c57..3d9482013 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,19 @@ internal class ImageFileDeltaTest { assertThat(imageFileDelta.detail()).contains(ImageFileDelta.Detail.AUTO) assertThat(imageFileDelta.fileId()).contains("file_id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageFileDelta = + ImageFileDelta.builder().detail(ImageFileDelta.Detail.AUTO).fileId("file_id").build() + + val roundtrippedImageFileDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageFileDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageFileDelta).isEqualTo(imageFileDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileTest.kt index 731c116d0..87799d0ec 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageFileTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class ImageFileTest { assertThat(imageFile.fileId()).isEqualTo("file_id") assertThat(imageFile.detail()).contains(ImageFile.Detail.AUTO) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageFile = ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + + val roundtrippedImageFile = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageFile), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageFile).isEqualTo(imageFile) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlockTest.kt index 932371c9e..7ac3494fa 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlContentBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -24,4 +26,26 @@ internal class ImageUrlContentBlockTest { ImageUrl.builder().url("https://example.com").detail(ImageUrl.Detail.AUTO).build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageUrlContentBlock = + ImageUrlContentBlock.builder() + .imageUrl( + ImageUrl.builder() + .url("https://example.com") + .detail(ImageUrl.Detail.AUTO) + .build() + ) + .build() + + val roundtrippedImageUrlContentBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageUrlContentBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageUrlContentBlock).isEqualTo(imageUrlContentBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlockTest.kt index 0fcfb2c79..5242597c1 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -21,4 +23,24 @@ internal class ImageUrlDeltaBlockTest { assertThat(imageUrlDeltaBlock.imageUrl()) .contains(ImageUrlDelta.builder().detail(ImageUrlDelta.Detail.AUTO).url("url").build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageUrlDeltaBlock = + ImageUrlDeltaBlock.builder() + .index(0L) + .imageUrl( + ImageUrlDelta.builder().detail(ImageUrlDelta.Detail.AUTO).url("url").build() + ) + .build() + + val roundtrippedImageUrlDeltaBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageUrlDeltaBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageUrlDeltaBlock).isEqualTo(imageUrlDeltaBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaTest.kt index cb4e7e4c9..6b8643723 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,19 @@ internal class ImageUrlDeltaTest { assertThat(imageUrlDelta.detail()).contains(ImageUrlDelta.Detail.AUTO) assertThat(imageUrlDelta.url()).contains("url") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageUrlDelta = + ImageUrlDelta.builder().detail(ImageUrlDelta.Detail.AUTO).url("url").build() + + val roundtrippedImageUrlDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageUrlDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageUrlDelta).isEqualTo(imageUrlDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlTest.kt index 90183f250..d2e8424e8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/ImageUrlTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,19 @@ internal class ImageUrlTest { assertThat(imageUrl.url()).isEqualTo("https://example.com") assertThat(imageUrl.detail()).contains(ImageUrl.Detail.AUTO) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imageUrl = + ImageUrl.builder().url("https://example.com").detail(ImageUrl.Detail.AUTO).build() + + val roundtrippedImageUrl = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imageUrl), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImageUrl).isEqualTo(imageUrl) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentDeltaTest.kt index 564be58f2..4e475efc9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentDeltaTest.kt @@ -2,14 +2,30 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class MessageContentDeltaTest { @Test fun ofImageFile() { - val imageFile = ImageFileDeltaBlock.builder().index(0L).build() + val imageFile = + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() val messageContentDelta = MessageContentDelta.ofImageFile(imageFile) @@ -19,9 +35,56 @@ internal class MessageContentDeltaTest { assertThat(messageContentDelta.imageUrl()).isEmpty } + @Test + fun ofImageFileRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentDelta = + MessageContentDelta.ofImageFile( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + + val roundtrippedMessageContentDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentDelta).isEqualTo(messageContentDelta) + } + @Test fun ofText() { - val text = TextDeltaBlock.builder().index(0L).build() + val text = + TextDeltaBlock.builder() + .index(0L) + .text( + TextDelta.builder() + .addAnnotation( + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + ) + .build() val messageContentDelta = MessageContentDelta.ofText(text) @@ -31,9 +94,47 @@ internal class MessageContentDeltaTest { assertThat(messageContentDelta.imageUrl()).isEmpty } + @Test + fun ofTextRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentDelta = + MessageContentDelta.ofText( + TextDeltaBlock.builder() + .index(0L) + .text( + TextDelta.builder() + .addAnnotation( + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + ) + .build() + ) + + val roundtrippedMessageContentDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentDelta).isEqualTo(messageContentDelta) + } + @Test fun ofRefusal() { - val refusal = RefusalDeltaBlock.builder().index(0L).build() + val refusal = RefusalDeltaBlock.builder().index(0L).refusal("refusal").build() val messageContentDelta = MessageContentDelta.ofRefusal(refusal) @@ -43,9 +144,32 @@ internal class MessageContentDeltaTest { assertThat(messageContentDelta.imageUrl()).isEmpty } + @Test + fun ofRefusalRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentDelta = + MessageContentDelta.ofRefusal( + RefusalDeltaBlock.builder().index(0L).refusal("refusal").build() + ) + + val roundtrippedMessageContentDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentDelta).isEqualTo(messageContentDelta) + } + @Test fun ofImageUrl() { - val imageUrl = ImageUrlDeltaBlock.builder().index(0L).build() + val imageUrl = + ImageUrlDeltaBlock.builder() + .index(0L) + .imageUrl( + ImageUrlDelta.builder().detail(ImageUrlDelta.Detail.AUTO).url("url").build() + ) + .build() val messageContentDelta = MessageContentDelta.ofImageUrl(imageUrl) @@ -54,4 +178,44 @@ internal class MessageContentDeltaTest { assertThat(messageContentDelta.refusal()).isEmpty assertThat(messageContentDelta.imageUrl()).contains(imageUrl) } + + @Test + fun ofImageUrlRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentDelta = + MessageContentDelta.ofImageUrl( + ImageUrlDeltaBlock.builder() + .index(0L) + .imageUrl( + ImageUrlDelta.builder().detail(ImageUrlDelta.Detail.AUTO).url("url").build() + ) + .build() + ) + + val roundtrippedMessageContentDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentDelta).isEqualTo(messageContentDelta) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val messageContentDelta = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { messageContentDelta.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParamTest.kt index aa6ff5a11..3975b6620 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentPartParamTest.kt @@ -2,8 +2,15 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class MessageContentPartParamTest { @@ -11,7 +18,9 @@ internal class MessageContentPartParamTest { fun ofImageFile() { val imageFile = ImageFileContentBlock.builder() - .imageFile(ImageFile.builder().fileId("file_id").build()) + .imageFile( + ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + ) .build() val messageContentPartParam = MessageContentPartParam.ofImageFile(imageFile) @@ -21,11 +30,37 @@ internal class MessageContentPartParamTest { assertThat(messageContentPartParam.text()).isEmpty } + @Test + fun ofImageFileRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentPartParam = + MessageContentPartParam.ofImageFile( + ImageFileContentBlock.builder() + .imageFile( + ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + ) + .build() + ) + + val roundtrippedMessageContentPartParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentPartParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentPartParam).isEqualTo(messageContentPartParam) + } + @Test fun ofImageUrl() { val imageUrl = ImageUrlContentBlock.builder() - .imageUrl(ImageUrl.builder().url("https://example.com").build()) + .imageUrl( + ImageUrl.builder() + .url("https://example.com") + .detail(ImageUrl.Detail.AUTO) + .build() + ) .build() val messageContentPartParam = MessageContentPartParam.ofImageUrl(imageUrl) @@ -35,6 +70,30 @@ internal class MessageContentPartParamTest { assertThat(messageContentPartParam.text()).isEmpty } + @Test + fun ofImageUrlRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentPartParam = + MessageContentPartParam.ofImageUrl( + ImageUrlContentBlock.builder() + .imageUrl( + ImageUrl.builder() + .url("https://example.com") + .detail(ImageUrl.Detail.AUTO) + .build() + ) + .build() + ) + + val roundtrippedMessageContentPartParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentPartParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentPartParam).isEqualTo(messageContentPartParam) + } + @Test fun ofText() { val text = TextContentBlockParam.builder().text("text").build() @@ -45,4 +104,37 @@ internal class MessageContentPartParamTest { assertThat(messageContentPartParam.imageUrl()).isEmpty assertThat(messageContentPartParam.text()).contains(text) } + + @Test + fun ofTextRoundtrip() { + val jsonMapper = jsonMapper() + val messageContentPartParam = + MessageContentPartParam.ofText(TextContentBlockParam.builder().text("text").build()) + + val roundtrippedMessageContentPartParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContentPartParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContentPartParam).isEqualTo(messageContentPartParam) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val messageContentPartParam = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { messageContentPartParam.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentTest.kt index 189c6ed76..c690b0fed 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageContentTest.kt @@ -2,8 +2,15 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class MessageContentTest { @@ -11,7 +18,9 @@ internal class MessageContentTest { fun ofImageFile() { val imageFile = ImageFileContentBlock.builder() - .imageFile(ImageFile.builder().fileId("file_id").build()) + .imageFile( + ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + ) .build() val messageContent = MessageContent.ofImageFile(imageFile) @@ -22,11 +31,37 @@ internal class MessageContentTest { assertThat(messageContent.refusal()).isEmpty } + @Test + fun ofImageFileRoundtrip() { + val jsonMapper = jsonMapper() + val messageContent = + MessageContent.ofImageFile( + ImageFileContentBlock.builder() + .imageFile( + ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + ) + .build() + ) + + val roundtrippedMessageContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContent).isEqualTo(messageContent) + } + @Test fun ofImageUrl() { val imageUrl = ImageUrlContentBlock.builder() - .imageUrl(ImageUrl.builder().url("https://example.com").build()) + .imageUrl( + ImageUrl.builder() + .url("https://example.com") + .detail(ImageUrl.Detail.AUTO) + .build() + ) .build() val messageContent = MessageContent.ofImageUrl(imageUrl) @@ -37,6 +72,30 @@ internal class MessageContentTest { assertThat(messageContent.refusal()).isEmpty } + @Test + fun ofImageUrlRoundtrip() { + val jsonMapper = jsonMapper() + val messageContent = + MessageContent.ofImageUrl( + ImageUrlContentBlock.builder() + .imageUrl( + ImageUrl.builder() + .url("https://example.com") + .detail(ImageUrl.Detail.AUTO) + .build() + ) + .build() + ) + + val roundtrippedMessageContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContent).isEqualTo(messageContent) + } + @Test fun ofText() { val text = @@ -68,6 +127,41 @@ internal class MessageContentTest { assertThat(messageContent.refusal()).isEmpty } + @Test + fun ofTextRoundtrip() { + val jsonMapper = jsonMapper() + val messageContent = + MessageContent.ofText( + TextContentBlock.builder() + .text( + Text.builder() + .addAnnotation( + FileCitationAnnotation.builder() + .endIndex(0L) + .fileCitation( + FileCitationAnnotation.FileCitation.builder() + .fileId("file_id") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + ) + .build() + ) + + val roundtrippedMessageContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContent).isEqualTo(messageContent) + } + @Test fun ofRefusal() { val refusal = RefusalContentBlock.builder().refusal("refusal").build() @@ -79,4 +173,37 @@ internal class MessageContentTest { assertThat(messageContent.text()).isEmpty assertThat(messageContent.refusal()).contains(refusal) } + + @Test + fun ofRefusalRoundtrip() { + val jsonMapper = jsonMapper() + val messageContent = + MessageContent.ofRefusal(RefusalContentBlock.builder().refusal("refusal").build()) + + val roundtrippedMessageContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageContent).isEqualTo(messageContent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val messageContent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { messageContent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeletedTest.kt index 24b4b4d28..fe930497c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class MessageDeletedTest { assertThat(messageDeleted.id()).isEqualTo("id") assertThat(messageDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val messageDeleted = MessageDeleted.builder().id("id").deleted(true).build() + + val roundtrippedMessageDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageDeleted).isEqualTo(messageDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEventTest.kt index 8f8f73843..e86267120 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -49,4 +51,37 @@ internal class MessageDeltaEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val messageDeltaEvent = + MessageDeltaEvent.builder() + .id("id") + .delta( + MessageDelta.builder() + .addContent( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + .role(MessageDelta.Role.USER) + .build() + ) + .build() + + val roundtrippedMessageDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageDeltaEvent).isEqualTo(messageDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaTest.kt index 72694efa6..43bd4fe39 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -42,4 +44,32 @@ internal class MessageDeltaTest { ) assertThat(messageDelta.role()).contains(MessageDelta.Role.USER) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val messageDelta = + MessageDelta.builder() + .addContent( + ImageFileDeltaBlock.builder() + .index(0L) + .imageFile( + ImageFileDelta.builder() + .detail(ImageFileDelta.Detail.AUTO) + .fileId("file_id") + .build() + ) + .build() + ) + .role(MessageDelta.Role.USER) + .build() + + val roundtrippedMessageDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageDelta).isEqualTo(messageDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageTest.kt index bf40919d9..b64ca0bf2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/MessageTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.beta.assistants.CodeInterpreterTool import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat @@ -86,4 +88,45 @@ internal class MessageTest { assertThat(message.status()).isEqualTo(Message.Status.IN_PROGRESS) assertThat(message.threadId()).isEqualTo("thread_id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val message = + Message.builder() + .id("id") + .assistantId("assistant_id") + .addAttachment( + Message.Attachment.builder() + .fileId("file_id") + .addTool(CodeInterpreterTool.builder().build()) + .build() + ) + .completedAt(0L) + .addImageFileContent( + ImageFile.builder().fileId("file_id").detail(ImageFile.Detail.AUTO).build() + ) + .createdAt(0L) + .incompleteAt(0L) + .incompleteDetails( + Message.IncompleteDetails.builder() + .reason(Message.IncompleteDetails.Reason.CONTENT_FILTER) + .build() + ) + .metadata( + Message.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .role(Message.Role.USER) + .runId("run_id") + .status(Message.Status.IN_PROGRESS) + .threadId("thread_id") + .build() + + val roundtrippedMessage = + jsonMapper.readValue(jsonMapper.writeValueAsString(message), jacksonTypeRef()) + + assertThat(roundtrippedMessage).isEqualTo(message) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlockTest.kt index 03fd91191..9b2602750 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalContentBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class RefusalContentBlockTest { assertThat(refusalContentBlock.refusal()).isEqualTo("refusal") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val refusalContentBlock = RefusalContentBlock.builder().refusal("refusal").build() + + val roundtrippedRefusalContentBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(refusalContentBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRefusalContentBlock).isEqualTo(refusalContentBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlockTest.kt index 587e2d25a..0e80f815f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/RefusalDeltaBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class RefusalDeltaBlockTest { assertThat(refusalDeltaBlock.index()).isEqualTo(0L) assertThat(refusalDeltaBlock.refusal()).contains("refusal") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val refusalDeltaBlock = RefusalDeltaBlock.builder().index(0L).refusal("refusal").build() + + val roundtrippedRefusalDeltaBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(refusalDeltaBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRefusalDeltaBlock).isEqualTo(refusalDeltaBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParamTest.kt index 7cd0324d0..c4f39e257 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class TextContentBlockParamTest { assertThat(textContentBlockParam.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val textContentBlockParam = TextContentBlockParam.builder().text("text").build() + + val roundtrippedTextContentBlockParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(textContentBlockParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTextContentBlockParam).isEqualTo(textContentBlockParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockTest.kt index f8a249d13..fa2f1ae0f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextContentBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -49,4 +51,37 @@ internal class TextContentBlockTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val textContentBlock = + TextContentBlock.builder() + .text( + Text.builder() + .addAnnotation( + FileCitationAnnotation.builder() + .endIndex(0L) + .fileCitation( + FileCitationAnnotation.FileCitation.builder() + .fileId("file_id") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + ) + .build() + + val roundtrippedTextContentBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(textContentBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTextContentBlock).isEqualTo(textContentBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlockTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlockTest.kt index a98f078a6..73f4293d9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlockTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaBlockTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -55,4 +57,40 @@ internal class TextDeltaBlockTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val textDeltaBlock = + TextDeltaBlock.builder() + .index(0L) + .text( + TextDelta.builder() + .addAnnotation( + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + ) + .build() + + val roundtrippedTextDeltaBlock = + jsonMapper.readValue( + jsonMapper.writeValueAsString(textDeltaBlock), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTextDeltaBlock).isEqualTo(textDeltaBlock) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaTest.kt index 00d171b42..f4af32bf7 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -48,4 +50,35 @@ internal class TextDeltaTest { ) assertThat(textDelta.value()).contains("value") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val textDelta = + TextDelta.builder() + .addAnnotation( + FileCitationDeltaAnnotation.builder() + .index(0L) + .endIndex(0L) + .fileCitation( + FileCitationDeltaAnnotation.FileCitation.builder() + .fileId("file_id") + .quote("quote") + .build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + + val roundtrippedTextDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(textDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTextDelta).isEqualTo(textDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextTest.kt index 5130596e8..06d3e0284 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/messages/TextTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.messages +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -39,4 +41,28 @@ internal class TextTest { ) assertThat(text.value()).isEqualTo("value") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val text = + Text.builder() + .addAnnotation( + FileCitationAnnotation.builder() + .endIndex(0L) + .fileCitation( + FileCitationAnnotation.FileCitation.builder().fileId("file_id").build() + ) + .startIndex(0L) + .text("text") + .build() + ) + .value("value") + .build() + + val roundtrippedText = + jsonMapper.readValue(jsonMapper.writeValueAsString(text), jacksonTypeRef()) + + assertThat(roundtrippedText).isEqualTo(text) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCallTest.kt index b84ee6340..03d08169e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RequiredActionFunctionToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -29,4 +31,28 @@ internal class RequiredActionFunctionToolCallTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val requiredActionFunctionToolCall = + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + + val roundtrippedRequiredActionFunctionToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(requiredActionFunctionToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRequiredActionFunctionToolCall) + .isEqualTo(requiredActionFunctionToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RunTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RunTest.kt index d3cd81455..479ba8380 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RunTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/RunTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.threads.runs +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.beta.assistants.AssistantTool import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.AssistantResponseFormatOption @@ -159,4 +161,85 @@ internal class RunTest { assertThat(run.temperature()).contains(0.0) assertThat(run.topP()).contains(0.0) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val run = + Run.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiresAt(0L) + .failedAt(0L) + .incompleteDetails( + Run.IncompleteDetails.builder() + .reason(Run.IncompleteDetails.Reason.MAX_COMPLETION_TOKENS) + .build() + ) + .instructions("instructions") + .lastError( + Run.LastError.builder() + .code(Run.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .maxCompletionTokens(256L) + .maxPromptTokens(256L) + .metadata( + Run.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model("model") + .parallelToolCalls(true) + .requiredAction( + Run.RequiredAction.builder() + .submitToolOutputs( + Run.RequiredAction.SubmitToolOutputs.builder() + .addToolCall( + RequiredActionFunctionToolCall.builder() + .id("id") + .function( + RequiredActionFunctionToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .responseFormatAuto() + .startedAt(0L) + .status(RunStatus.QUEUED) + .threadId("thread_id") + .toolChoice(AssistantToolChoiceOption.Auto.NONE) + .addTool(CodeInterpreterTool.builder().build()) + .truncationStrategy( + Run.TruncationStrategy.builder() + .type(Run.TruncationStrategy.Type.AUTO) + .lastMessages(1L) + .build() + ) + .usage( + Run.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .temperature(0.0) + .topP(0.0) + .build() + + val roundtrippedRun = + jsonMapper.readValue(jsonMapper.writeValueAsString(run), jacksonTypeRef()) + + assertThat(roundtrippedRun).isEqualTo(run) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogsTest.kt index 4d108b704..bfb38cc4d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterLogsTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class CodeInterpreterLogsTest { assertThat(codeInterpreterLogs.index()).isEqualTo(0L) assertThat(codeInterpreterLogs.logs()).contains("logs") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val codeInterpreterLogs = CodeInterpreterLogs.builder().index(0L).logs("logs").build() + + val roundtrippedCodeInterpreterLogs = + jsonMapper.readValue( + jsonMapper.writeValueAsString(codeInterpreterLogs), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCodeInterpreterLogs).isEqualTo(codeInterpreterLogs) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImageTest.kt index 1a1057472..0b869f498 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterOutputImageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -19,4 +21,22 @@ internal class CodeInterpreterOutputImageTest { assertThat(codeInterpreterOutputImage.image()) .contains(CodeInterpreterOutputImage.Image.builder().fileId("file_id").build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val codeInterpreterOutputImage = + CodeInterpreterOutputImage.builder() + .index(0L) + .image(CodeInterpreterOutputImage.Image.builder().fileId("file_id").build()) + .build() + + val roundtrippedCodeInterpreterOutputImage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(codeInterpreterOutputImage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCodeInterpreterOutputImage).isEqualTo(codeInterpreterOutputImage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDeltaTest.kt index e04eeaa1a..52c758c77 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -31,4 +33,28 @@ internal class CodeInterpreterToolCallDeltaTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val codeInterpreterToolCallDelta = + CodeInterpreterToolCallDelta.builder() + .index(0L) + .id("id") + .codeInterpreter( + CodeInterpreterToolCallDelta.CodeInterpreter.builder() + .input("input") + .addOutput(CodeInterpreterLogs.builder().index(0L).logs("logs").build()) + .build() + ) + .build() + + val roundtrippedCodeInterpreterToolCallDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(codeInterpreterToolCallDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCodeInterpreterToolCallDelta).isEqualTo(codeInterpreterToolCallDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallTest.kt index d71b4a526..9f9aded0c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/CodeInterpreterToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -29,4 +31,27 @@ internal class CodeInterpreterToolCallTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val codeInterpreterToolCall = + CodeInterpreterToolCall.builder() + .id("id") + .codeInterpreter( + CodeInterpreterToolCall.CodeInterpreter.builder() + .input("input") + .addLogsOutput("logs") + .build() + ) + .build() + + val roundtrippedCodeInterpreterToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(codeInterpreterToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCodeInterpreterToolCall).isEqualTo(codeInterpreterToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDeltaTest.kt index 81dd91502..bf5308b79 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallDeltaTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,23 @@ internal class FileSearchToolCallDeltaTest { assertThat(fileSearchToolCallDelta.index()).isEqualTo(0L) assertThat(fileSearchToolCallDelta.id()).contains("id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileSearchToolCallDelta = + FileSearchToolCallDelta.builder() + .fileSearch(JsonValue.from(mapOf())) + .index(0L) + .id("id") + .build() + + val roundtrippedFileSearchToolCallDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileSearchToolCallDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileSearchToolCallDelta).isEqualTo(fileSearchToolCallDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallTest.kt index 943413e11..fa57a2d0a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FileSearchToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -65,4 +67,46 @@ internal class FileSearchToolCallTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileSearchToolCall = + FileSearchToolCall.builder() + .id("id") + .fileSearch( + FileSearchToolCall.FileSearch.builder() + .rankingOptions( + FileSearchToolCall.FileSearch.RankingOptions.builder() + .ranker(FileSearchToolCall.FileSearch.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .addResult( + FileSearchToolCall.FileSearch.Result.builder() + .fileId("file_id") + .fileName("file_name") + .score(0.0) + .addContent( + FileSearchToolCall.FileSearch.Result.Content.builder() + .text("text") + .type( + FileSearchToolCall.FileSearch.Result.Content.Type.TEXT + ) + .build() + ) + .build() + ) + .build() + ) + .build() + + val roundtrippedFileSearchToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileSearchToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileSearchToolCall).isEqualTo(fileSearchToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDeltaTest.kt index c16177f1a..5cbdc50f3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,29 @@ internal class FunctionToolCallDeltaTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val functionToolCallDelta = + FunctionToolCallDelta.builder() + .index(0L) + .id("id") + .function( + FunctionToolCallDelta.Function.builder() + .arguments("arguments") + .name("name") + .output("output") + .build() + ) + .build() + + val roundtrippedFunctionToolCallDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(functionToolCallDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFunctionToolCallDelta).isEqualTo(functionToolCallDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallTest.kt index 927b51aee..05dc41d38 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/FunctionToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -31,4 +33,28 @@ internal class FunctionToolCallTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val functionToolCall = + FunctionToolCall.builder() + .id("id") + .function( + FunctionToolCall.Function.builder() + .arguments("arguments") + .name("name") + .output("output") + .build() + ) + .build() + + val roundtrippedFunctionToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(functionToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFunctionToolCall).isEqualTo(functionToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetailsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetailsTest.kt index d6212467a..d4bf10822 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetailsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/MessageCreationStepDetailsTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -23,4 +25,25 @@ internal class MessageCreationStepDetailsTest { MessageCreationStepDetails.MessageCreation.builder().messageId("message_id").build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val messageCreationStepDetails = + MessageCreationStepDetails.builder() + .messageCreation( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + + val roundtrippedMessageCreationStepDetails = + jsonMapper.readValue( + jsonMapper.writeValueAsString(messageCreationStepDetails), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMessageCreationStepDetails).isEqualTo(messageCreationStepDetails) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEventTest.kt index 1807f8a9e..10ccd1d11 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -43,4 +45,34 @@ internal class RunStepDeltaEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val runStepDeltaEvent = + RunStepDeltaEvent.builder() + .id("id") + .delta( + RunStepDelta.builder() + .stepDetails( + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + ) + .build() + ) + .build() + + val roundtrippedRunStepDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepDeltaEvent).isEqualTo(runStepDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDeltaTest.kt index 06b8c74c4..8b1aa7220 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaMessageDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -23,4 +25,25 @@ internal class RunStepDeltaMessageDeltaTest { RunStepDeltaMessageDelta.MessageCreation.builder().messageId("message_id").build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val runStepDeltaMessageDelta = + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + + val roundtrippedRunStepDeltaMessageDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepDeltaMessageDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepDeltaMessageDelta).isEqualTo(runStepDeltaMessageDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaTest.kt index fa5e919d9..db89ad662 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepDeltaTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -35,4 +37,29 @@ internal class RunStepDeltaTest { ) ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val runStepDelta = + RunStepDelta.builder() + .stepDetails( + RunStepDeltaMessageDelta.builder() + .messageCreation( + RunStepDeltaMessageDelta.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .build() + ) + .build() + + val roundtrippedRunStepDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runStepDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunStepDelta).isEqualTo(runStepDelta) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepTest.kt index e116ca3f1..5676de463 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/RunStepTest.kt @@ -2,7 +2,9 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -93,4 +95,51 @@ internal class RunStepTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val runStep = + RunStep.builder() + .id("id") + .assistantId("assistant_id") + .cancelledAt(0L) + .completedAt(0L) + .createdAt(0L) + .expiredAt(0L) + .failedAt(0L) + .lastError( + RunStep.LastError.builder() + .code(RunStep.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .metadata( + RunStep.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .runId("run_id") + .status(RunStep.Status.IN_PROGRESS) + .messageCreationStepDetails( + MessageCreationStepDetails.MessageCreation.builder() + .messageId("message_id") + .build() + ) + .threadId("thread_id") + .type(RunStep.Type.MESSAGE_CREATION) + .usage( + RunStep.Usage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .build() + ) + .build() + + val roundtrippedRunStep = + jsonMapper.readValue(jsonMapper.writeValueAsString(runStep), jacksonTypeRef()) + + assertThat(roundtrippedRunStep).isEqualTo(runStep) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObjectTest.kt index 4b6a0fef3..f385e0197 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaObjectTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -46,4 +48,34 @@ internal class ToolCallDeltaObjectTest { ) ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val toolCallDeltaObject = + ToolCallDeltaObject.builder() + .addToolCall( + CodeInterpreterToolCallDelta.builder() + .index(0L) + .id("id") + .codeInterpreter( + CodeInterpreterToolCallDelta.CodeInterpreter.builder() + .input("input") + .addOutput( + CodeInterpreterLogs.builder().index(0L).logs("logs").build() + ) + .build() + ) + .build() + ) + .build() + + val roundtrippedToolCallDeltaObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCallDeltaObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCallDeltaObject).isEqualTo(toolCallDeltaObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaTest.kt index 7b6fa63e4..aff6ab59e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallDeltaTest.kt @@ -2,15 +2,31 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ToolCallDeltaTest { @Test fun ofCodeInterpreter() { - val codeInterpreter = CodeInterpreterToolCallDelta.builder().index(0L).build() + val codeInterpreter = + CodeInterpreterToolCallDelta.builder() + .index(0L) + .id("id") + .codeInterpreter( + CodeInterpreterToolCallDelta.CodeInterpreter.builder() + .input("input") + .addOutput(CodeInterpreterLogs.builder().index(0L).logs("logs").build()) + .build() + ) + .build() val toolCallDelta = ToolCallDelta.ofCodeInterpreter(codeInterpreter) @@ -19,12 +35,39 @@ internal class ToolCallDeltaTest { assertThat(toolCallDelta.function()).isEmpty } + @Test + fun ofCodeInterpreterRoundtrip() { + val jsonMapper = jsonMapper() + val toolCallDelta = + ToolCallDelta.ofCodeInterpreter( + CodeInterpreterToolCallDelta.builder() + .index(0L) + .id("id") + .codeInterpreter( + CodeInterpreterToolCallDelta.CodeInterpreter.builder() + .input("input") + .addOutput(CodeInterpreterLogs.builder().index(0L).logs("logs").build()) + .build() + ) + .build() + ) + + val roundtrippedToolCallDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCallDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCallDelta).isEqualTo(toolCallDelta) + } + @Test fun ofFileSearch() { val fileSearch = FileSearchToolCallDelta.builder() .fileSearch(JsonValue.from(mapOf())) .index(0L) + .id("id") .build() val toolCallDelta = ToolCallDelta.ofFileSearch(fileSearch) @@ -34,9 +77,41 @@ internal class ToolCallDeltaTest { assertThat(toolCallDelta.function()).isEmpty } + @Test + fun ofFileSearchRoundtrip() { + val jsonMapper = jsonMapper() + val toolCallDelta = + ToolCallDelta.ofFileSearch( + FileSearchToolCallDelta.builder() + .fileSearch(JsonValue.from(mapOf())) + .index(0L) + .id("id") + .build() + ) + + val roundtrippedToolCallDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCallDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCallDelta).isEqualTo(toolCallDelta) + } + @Test fun ofFunction() { - val function = FunctionToolCallDelta.builder().index(0L).build() + val function = + FunctionToolCallDelta.builder() + .index(0L) + .id("id") + .function( + FunctionToolCallDelta.Function.builder() + .arguments("arguments") + .name("name") + .output("output") + .build() + ) + .build() val toolCallDelta = ToolCallDelta.ofFunction(function) @@ -44,4 +119,49 @@ internal class ToolCallDeltaTest { assertThat(toolCallDelta.fileSearch()).isEmpty assertThat(toolCallDelta.function()).contains(function) } + + @Test + fun ofFunctionRoundtrip() { + val jsonMapper = jsonMapper() + val toolCallDelta = + ToolCallDelta.ofFunction( + FunctionToolCallDelta.builder() + .index(0L) + .id("id") + .function( + FunctionToolCallDelta.Function.builder() + .arguments("arguments") + .name("name") + .output("output") + .build() + ) + .build() + ) + + val roundtrippedToolCallDelta = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCallDelta), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCallDelta).isEqualTo(toolCallDelta) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val toolCallDelta = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { toolCallDelta.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallTest.kt index 0f3fe9cfb..6cb958877 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallTest.kt @@ -2,8 +2,15 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ToolCallTest { @@ -27,12 +34,61 @@ internal class ToolCallTest { assertThat(toolCall.function()).isEmpty } + @Test + fun ofCodeInterpreterRoundtrip() { + val jsonMapper = jsonMapper() + val toolCall = + ToolCall.ofCodeInterpreter( + CodeInterpreterToolCall.builder() + .id("id") + .codeInterpreter( + CodeInterpreterToolCall.CodeInterpreter.builder() + .input("input") + .addLogsOutput("logs") + .build() + ) + .build() + ) + + val roundtrippedToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCall).isEqualTo(toolCall) + } + @Test fun ofFileSearch() { val fileSearch = FileSearchToolCall.builder() .id("id") - .fileSearch(FileSearchToolCall.FileSearch.builder().build()) + .fileSearch( + FileSearchToolCall.FileSearch.builder() + .rankingOptions( + FileSearchToolCall.FileSearch.RankingOptions.builder() + .ranker(FileSearchToolCall.FileSearch.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .addResult( + FileSearchToolCall.FileSearch.Result.builder() + .fileId("file_id") + .fileName("file_name") + .score(0.0) + .addContent( + FileSearchToolCall.FileSearch.Result.Content.builder() + .text("text") + .type( + FileSearchToolCall.FileSearch.Result.Content.Type.TEXT + ) + .build() + ) + .build() + ) + .build() + ) .build() val toolCall = ToolCall.ofFileSearch(fileSearch) @@ -42,6 +98,53 @@ internal class ToolCallTest { assertThat(toolCall.function()).isEmpty } + @Test + fun ofFileSearchRoundtrip() { + val jsonMapper = jsonMapper() + val toolCall = + ToolCall.ofFileSearch( + FileSearchToolCall.builder() + .id("id") + .fileSearch( + FileSearchToolCall.FileSearch.builder() + .rankingOptions( + FileSearchToolCall.FileSearch.RankingOptions.builder() + .ranker( + FileSearchToolCall.FileSearch.RankingOptions.Ranker.AUTO + ) + .scoreThreshold(0.0) + .build() + ) + .addResult( + FileSearchToolCall.FileSearch.Result.builder() + .fileId("file_id") + .fileName("file_name") + .score(0.0) + .addContent( + FileSearchToolCall.FileSearch.Result.Content.builder() + .text("text") + .type( + FileSearchToolCall.FileSearch.Result.Content.Type + .TEXT + ) + .build() + ) + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCall).isEqualTo(toolCall) + } + @Test fun ofFunction() { val function = @@ -62,4 +165,47 @@ internal class ToolCallTest { assertThat(toolCall.fileSearch()).isEmpty assertThat(toolCall.function()).contains(function) } + + @Test + fun ofFunctionRoundtrip() { + val jsonMapper = jsonMapper() + val toolCall = + ToolCall.ofFunction( + FunctionToolCall.builder() + .id("id") + .function( + FunctionToolCall.Function.builder() + .arguments("arguments") + .name("name") + .output("output") + .build() + ) + .build() + ) + + val roundtrippedToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCall).isEqualTo(toolCall) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val toolCall = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { toolCall.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetailsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetailsTest.kt index de1bb486c..8dff216e3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetailsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/beta/threads/runs/steps/ToolCallsStepDetailsTest.kt @@ -2,6 +2,8 @@ package com.openai.models.beta.threads.runs.steps +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -39,4 +41,31 @@ internal class ToolCallsStepDetailsTest { ) ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val toolCallsStepDetails = + ToolCallsStepDetails.builder() + .addToolCall( + CodeInterpreterToolCall.builder() + .id("id") + .codeInterpreter( + CodeInterpreterToolCall.CodeInterpreter.builder() + .input("input") + .addLogsOutput("logs") + .build() + ) + .build() + ) + .build() + + val roundtrippedToolCallsStepDetails = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolCallsStepDetails), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolCallsStepDetails).isEqualTo(toolCallsStepDetails) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParamTest.kt index 6762b976a..65af7f4a9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAssistantMessageParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -61,4 +63,42 @@ internal class ChatCompletionAssistantMessageParamTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionAssistantMessageParam = + ChatCompletionAssistantMessageParam.builder() + .audio(ChatCompletionAssistantMessageParam.Audio.builder().id("id").build()) + .content("string") + .functionCall( + ChatCompletionAssistantMessageParam.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .name("name") + .refusal("refusal") + .addToolCall( + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + + val roundtrippedChatCompletionAssistantMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionAssistantMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionAssistantMessageParam) + .isEqualTo(chatCompletionAssistantMessageParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParamTest.kt index 36932317f..84a4b014b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,4 +20,22 @@ internal class ChatCompletionAudioParamTest { assertThat(chatCompletionAudioParam.format()).isEqualTo(ChatCompletionAudioParam.Format.WAV) assertThat(chatCompletionAudioParam.voice()).isEqualTo(ChatCompletionAudioParam.Voice.ALLOY) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionAudioParam = + ChatCompletionAudioParam.builder() + .format(ChatCompletionAudioParam.Format.WAV) + .voice(ChatCompletionAudioParam.Voice.ALLOY) + .build() + + val roundtrippedChatCompletionAudioParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionAudioParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionAudioParam).isEqualTo(chatCompletionAudioParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioTest.kt index 022b30ccc..92ba98c9f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionAudioTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,24 @@ internal class ChatCompletionAudioTest { assertThat(chatCompletionAudio.expiresAt()).isEqualTo(0L) assertThat(chatCompletionAudio.transcript()).isEqualTo("transcript") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionAudio = + ChatCompletionAudio.builder() + .id("id") + .data("data") + .expiresAt(0L) + .transcript("transcript") + .build() + + val roundtrippedChatCompletionAudio = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionAudio), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionAudio).isEqualTo(chatCompletionAudio) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionChunkTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionChunkTest.kt index 0146eec28..73f3fa26a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionChunkTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionChunkTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.completions.CompletionUsage import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -201,4 +203,113 @@ internal class ChatCompletionChunkTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionChunk = + ChatCompletionChunk.builder() + .id("id") + .addChoice( + ChatCompletionChunk.Choice.builder() + .delta( + ChatCompletionChunk.Choice.Delta.builder() + .content("content") + .functionCall( + ChatCompletionChunk.Choice.Delta.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .refusal("refusal") + .role(ChatCompletionChunk.Choice.Delta.Role.DEVELOPER) + .addToolCall( + ChatCompletionChunk.Choice.Delta.ToolCall.builder() + .index(0L) + .id("id") + .function( + ChatCompletionChunk.Choice.Delta.ToolCall.Function + .builder() + .arguments("arguments") + .name("name") + .build() + ) + .type( + ChatCompletionChunk.Choice.Delta.ToolCall.Type.FUNCTION + ) + .build() + ) + .build() + ) + .finishReason(ChatCompletionChunk.Choice.FinishReason.STOP) + .index(0L) + .logprobs( + ChatCompletionChunk.Choice.Logprobs.builder() + .addContent( + ChatCompletionTokenLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .addTopLogprob( + ChatCompletionTokenLogprob.TopLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .build() + ) + .build() + ) + .addRefusal( + ChatCompletionTokenLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .addTopLogprob( + ChatCompletionTokenLogprob.TopLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .created(0L) + .model("model") + .serviceTier(ChatCompletionChunk.ServiceTier.SCALE) + .systemFingerprint("system_fingerprint") + .usage( + CompletionUsage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .completionTokensDetails( + CompletionUsage.CompletionTokensDetails.builder() + .acceptedPredictionTokens(0L) + .audioTokens(0L) + .reasoningTokens(0L) + .rejectedPredictionTokens(0L) + .build() + ) + .promptTokensDetails( + CompletionUsage.PromptTokensDetails.builder() + .audioTokens(0L) + .cachedTokens(0L) + .build() + ) + .build() + ) + .build() + + val roundtrippedChatCompletionChunk = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionChunk), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionChunk).isEqualTo(chatCompletionChunk) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImageTest.kt index 2e4aebe80..4844a05ee 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartImageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -27,4 +29,27 @@ internal class ChatCompletionContentPartImageTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPartImage = + ChatCompletionContentPartImage.builder() + .imageUrl( + ChatCompletionContentPartImage.ImageUrl.builder() + .url("https://example.com") + .detail(ChatCompletionContentPartImage.ImageUrl.Detail.AUTO) + .build() + ) + .build() + + val roundtrippedChatCompletionContentPartImage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPartImage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPartImage) + .isEqualTo(chatCompletionContentPartImage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudioTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudioTest.kt index c25eb84f4..e05d04f64 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudioTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartInputAudioTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -27,4 +29,27 @@ internal class ChatCompletionContentPartInputAudioTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPartInputAudio = + ChatCompletionContentPartInputAudio.builder() + .inputAudio( + ChatCompletionContentPartInputAudio.InputAudio.builder() + .data("data") + .format(ChatCompletionContentPartInputAudio.InputAudio.Format.WAV) + .build() + ) + .build() + + val roundtrippedChatCompletionContentPartInputAudio = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPartInputAudio), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPartInputAudio) + .isEqualTo(chatCompletionContentPartInputAudio) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusalTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusalTest.kt index 673cbe165..b5459ce19 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusalTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartRefusalTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,20 @@ internal class ChatCompletionContentPartRefusalTest { assertThat(chatCompletionContentPartRefusal.refusal()).isEqualTo("refusal") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPartRefusal = + ChatCompletionContentPartRefusal.builder().refusal("refusal").build() + + val roundtrippedChatCompletionContentPartRefusal = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPartRefusal), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPartRefusal) + .isEqualTo(chatCompletionContentPartRefusal) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTest.kt index c6740ebf9..6fcb45bc0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTest.kt @@ -2,8 +2,15 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ChatCompletionContentPartTest { @@ -19,6 +26,23 @@ internal class ChatCompletionContentPartTest { assertThat(chatCompletionContentPart.file()).isEmpty } + @Test + fun ofTextRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPart = + ChatCompletionContentPart.ofText( + ChatCompletionContentPartText.builder().text("text").build() + ) + + val roundtrippedChatCompletionContentPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPart).isEqualTo(chatCompletionContentPart) + } + @Test fun ofImageUrl() { val imageUrl = @@ -26,6 +50,7 @@ internal class ChatCompletionContentPartTest { .imageUrl( ChatCompletionContentPartImage.ImageUrl.builder() .url("https://example.com") + .detail(ChatCompletionContentPartImage.ImageUrl.Detail.AUTO) .build() ) .build() @@ -38,6 +63,30 @@ internal class ChatCompletionContentPartTest { assertThat(chatCompletionContentPart.file()).isEmpty } + @Test + fun ofImageUrlRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPart = + ChatCompletionContentPart.ofImageUrl( + ChatCompletionContentPartImage.builder() + .imageUrl( + ChatCompletionContentPartImage.ImageUrl.builder() + .url("https://example.com") + .detail(ChatCompletionContentPartImage.ImageUrl.Detail.AUTO) + .build() + ) + .build() + ) + + val roundtrippedChatCompletionContentPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPart).isEqualTo(chatCompletionContentPart) + } + @Test fun ofInputAudio() { val inputAudio = @@ -58,11 +107,41 @@ internal class ChatCompletionContentPartTest { assertThat(chatCompletionContentPart.file()).isEmpty } + @Test + fun ofInputAudioRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPart = + ChatCompletionContentPart.ofInputAudio( + ChatCompletionContentPartInputAudio.builder() + .inputAudio( + ChatCompletionContentPartInputAudio.InputAudio.builder() + .data("data") + .format(ChatCompletionContentPartInputAudio.InputAudio.Format.WAV) + .build() + ) + .build() + ) + + val roundtrippedChatCompletionContentPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPart).isEqualTo(chatCompletionContentPart) + } + @Test fun ofFile() { val file = ChatCompletionContentPart.File.builder() - .file(ChatCompletionContentPart.File.FileObject.builder().build()) + .file( + ChatCompletionContentPart.File.FileObject.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() + ) .build() val chatCompletionContentPart = ChatCompletionContentPart.ofFile(file) @@ -72,4 +151,47 @@ internal class ChatCompletionContentPartTest { assertThat(chatCompletionContentPart.inputAudio()).isEmpty assertThat(chatCompletionContentPart.file()).contains(file) } + + @Test + fun ofFileRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPart = + ChatCompletionContentPart.ofFile( + ChatCompletionContentPart.File.builder() + .file( + ChatCompletionContentPart.File.FileObject.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() + ) + .build() + ) + + val roundtrippedChatCompletionContentPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPart).isEqualTo(chatCompletionContentPart) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val chatCompletionContentPart = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { chatCompletionContentPart.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTextTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTextTest.kt index b16001782..37c242761 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTextTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionContentPartTextTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,20 @@ internal class ChatCompletionContentPartTextTest { assertThat(chatCompletionContentPartText.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionContentPartText = + ChatCompletionContentPartText.builder().text("text").build() + + val roundtrippedChatCompletionContentPartText = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionContentPartText), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionContentPartText) + .isEqualTo(chatCompletionContentPartText) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeletedTest.kt index 4c1c5abed..9c5c64dec 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class ChatCompletionDeletedTest { assertThat(chatCompletionDeleted.id()).isEqualTo("id") assertThat(chatCompletionDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionDeleted = ChatCompletionDeleted.builder().id("id").deleted(true).build() + + val roundtrippedChatCompletionDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionDeleted).isEqualTo(chatCompletionDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParamTest.kt index 4f652b0ee..fb6f24872 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionDeveloperMessageParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,20 @@ internal class ChatCompletionDeveloperMessageParamTest { .isEqualTo(ChatCompletionDeveloperMessageParam.Content.ofText("string")) assertThat(chatCompletionDeveloperMessageParam.name()).contains("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionDeveloperMessageParam = + ChatCompletionDeveloperMessageParam.builder().content("string").name("name").build() + + val roundtrippedChatCompletionDeveloperMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionDeveloperMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionDeveloperMessageParam) + .isEqualTo(chatCompletionDeveloperMessageParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOptionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOptionTest.kt index b7f1706fb..17ce87806 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOptionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionCallOptionTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,20 @@ internal class ChatCompletionFunctionCallOptionTest { assertThat(chatCompletionFunctionCallOption.name()).isEqualTo("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionFunctionCallOption = + ChatCompletionFunctionCallOption.builder().name("name").build() + + val roundtrippedChatCompletionFunctionCallOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionFunctionCallOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionFunctionCallOption) + .isEqualTo(chatCompletionFunctionCallOption) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParamTest.kt index 63ffac402..82d0dffb8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionFunctionMessageParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ChatCompletionFunctionMessageParamTest { assertThat(chatCompletionFunctionMessageParam.content()).contains("content") assertThat(chatCompletionFunctionMessageParam.name()).isEqualTo("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionFunctionMessageParam = + ChatCompletionFunctionMessageParam.builder().content("content").name("name").build() + + val roundtrippedChatCompletionFunctionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionFunctionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionFunctionMessageParam) + .isEqualTo(chatCompletionFunctionMessageParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParamTest.kt index f2fd3e42c..17060eb93 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageParamTest.kt @@ -2,14 +2,22 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ChatCompletionMessageParamTest { @Test fun ofDeveloper() { - val developer = ChatCompletionDeveloperMessageParam.builder().content("string").build() + val developer = + ChatCompletionDeveloperMessageParam.builder().content("string").name("name").build() val chatCompletionMessageParam = ChatCompletionMessageParam.ofDeveloper(developer) @@ -21,9 +29,27 @@ internal class ChatCompletionMessageParamTest { assertThat(chatCompletionMessageParam.function()).isEmpty } + @Test + fun ofDeveloperRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageParam = + ChatCompletionMessageParam.ofDeveloper( + ChatCompletionDeveloperMessageParam.builder().content("string").name("name").build() + ) + + val roundtrippedChatCompletionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageParam).isEqualTo(chatCompletionMessageParam) + } + @Test fun ofSystem() { - val system = ChatCompletionSystemMessageParam.builder().content("string").build() + val system = + ChatCompletionSystemMessageParam.builder().content("string").name("name").build() val chatCompletionMessageParam = ChatCompletionMessageParam.ofSystem(system) @@ -35,9 +61,26 @@ internal class ChatCompletionMessageParamTest { assertThat(chatCompletionMessageParam.function()).isEmpty } + @Test + fun ofSystemRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageParam = + ChatCompletionMessageParam.ofSystem( + ChatCompletionSystemMessageParam.builder().content("string").name("name").build() + ) + + val roundtrippedChatCompletionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageParam).isEqualTo(chatCompletionMessageParam) + } + @Test fun ofUser() { - val user = ChatCompletionUserMessageParam.builder().content("string").build() + val user = ChatCompletionUserMessageParam.builder().content("string").name("name").build() val chatCompletionMessageParam = ChatCompletionMessageParam.ofUser(user) @@ -49,9 +92,49 @@ internal class ChatCompletionMessageParamTest { assertThat(chatCompletionMessageParam.function()).isEmpty } + @Test + fun ofUserRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageParam = + ChatCompletionMessageParam.ofUser( + ChatCompletionUserMessageParam.builder().content("string").name("name").build() + ) + + val roundtrippedChatCompletionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageParam).isEqualTo(chatCompletionMessageParam) + } + @Test fun ofAssistant() { - val assistant = ChatCompletionAssistantMessageParam.builder().build() + val assistant = + ChatCompletionAssistantMessageParam.builder() + .audio(ChatCompletionAssistantMessageParam.Audio.builder().id("id").build()) + .content("string") + .functionCall( + ChatCompletionAssistantMessageParam.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .name("name") + .refusal("refusal") + .addToolCall( + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() val chatCompletionMessageParam = ChatCompletionMessageParam.ofAssistant(assistant) @@ -63,6 +146,45 @@ internal class ChatCompletionMessageParamTest { assertThat(chatCompletionMessageParam.function()).isEmpty } + @Test + fun ofAssistantRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageParam = + ChatCompletionMessageParam.ofAssistant( + ChatCompletionAssistantMessageParam.builder() + .audio(ChatCompletionAssistantMessageParam.Audio.builder().id("id").build()) + .content("string") + .functionCall( + ChatCompletionAssistantMessageParam.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .name("name") + .refusal("refusal") + .addToolCall( + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedChatCompletionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageParam).isEqualTo(chatCompletionMessageParam) + } + @Test fun ofTool() { val tool = @@ -81,6 +203,26 @@ internal class ChatCompletionMessageParamTest { assertThat(chatCompletionMessageParam.function()).isEmpty } + @Test + fun ofToolRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageParam = + ChatCompletionMessageParam.ofTool( + ChatCompletionToolMessageParam.builder() + .content("string") + .toolCallId("tool_call_id") + .build() + ) + + val roundtrippedChatCompletionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageParam).isEqualTo(chatCompletionMessageParam) + } + @Test fun ofFunction() { val function = @@ -95,4 +237,39 @@ internal class ChatCompletionMessageParamTest { assertThat(chatCompletionMessageParam.tool()).isEmpty assertThat(chatCompletionMessageParam.function()).contains(function) } + + @Test + fun ofFunctionRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageParam = + ChatCompletionMessageParam.ofFunction( + ChatCompletionFunctionMessageParam.builder().content("content").name("name").build() + ) + + val roundtrippedChatCompletionMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageParam).isEqualTo(chatCompletionMessageParam) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val chatCompletionMessageParam = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { chatCompletionMessageParam.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageTest.kt index 2014e67b4..9901c8f54 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -97,4 +99,59 @@ internal class ChatCompletionMessageTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessage = + ChatCompletionMessage.builder() + .content("content") + .refusal("refusal") + .addAnnotation( + ChatCompletionMessage.Annotation.builder() + .urlCitation( + ChatCompletionMessage.Annotation.UrlCitation.builder() + .endIndex(0L) + .startIndex(0L) + .title("title") + .url("url") + .build() + ) + .build() + ) + .audio( + ChatCompletionAudio.builder() + .id("id") + .data("data") + .expiresAt(0L) + .transcript("transcript") + .build() + ) + .functionCall( + ChatCompletionMessage.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .addToolCall( + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + + val roundtrippedChatCompletionMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessage).isEqualTo(chatCompletionMessage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCallTest.kt index c0facf3b6..0806b1cd7 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionMessageToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -29,4 +31,28 @@ internal class ChatCompletionMessageToolCallTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionMessageToolCall = + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + + val roundtrippedChatCompletionMessageToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionMessageToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionMessageToolCall) + .isEqualTo(chatCompletionMessageToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoiceTest.kt index ac1d2b933..951de8646 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionNamedToolChoiceTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -17,4 +19,22 @@ internal class ChatCompletionNamedToolChoiceTest { assertThat(chatCompletionNamedToolChoice.function()) .isEqualTo(ChatCompletionNamedToolChoice.Function.builder().name("name").build()) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionNamedToolChoice = + ChatCompletionNamedToolChoice.builder() + .function(ChatCompletionNamedToolChoice.Function.builder().name("name").build()) + .build() + + val roundtrippedChatCompletionNamedToolChoice = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionNamedToolChoice), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionNamedToolChoice) + .isEqualTo(chatCompletionNamedToolChoice) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContentTest.kt index e7120e988..74357a3d2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionPredictionContentTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ChatCompletionPredictionContentTest { assertThat(chatCompletionPredictionContent.content()) .isEqualTo(ChatCompletionPredictionContent.Content.ofText("string")) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionPredictionContent = + ChatCompletionPredictionContent.builder().content("string").build() + + val roundtrippedChatCompletionPredictionContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionPredictionContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionPredictionContent) + .isEqualTo(chatCompletionPredictionContent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessageTest.kt index a60a700ba..6e5a03a3b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStoreMessageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -99,4 +101,60 @@ internal class ChatCompletionStoreMessageTest { ) assertThat(chatCompletionStoreMessage.id()).isEqualTo("id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionStoreMessage = + ChatCompletionStoreMessage.builder() + .content("content") + .refusal("refusal") + .addAnnotation( + ChatCompletionMessage.Annotation.builder() + .urlCitation( + ChatCompletionMessage.Annotation.UrlCitation.builder() + .endIndex(0L) + .startIndex(0L) + .title("title") + .url("url") + .build() + ) + .build() + ) + .audio( + ChatCompletionAudio.builder() + .id("id") + .data("data") + .expiresAt(0L) + .transcript("transcript") + .build() + ) + .functionCall( + ChatCompletionMessage.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .addToolCall( + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .id("id") + .build() + + val roundtrippedChatCompletionStoreMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionStoreMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionStoreMessage).isEqualTo(chatCompletionStoreMessage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptionsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptionsTest.kt index 75b7d0b4c..1041eb252 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptionsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionStreamOptionsTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,19 @@ internal class ChatCompletionStreamOptionsTest { assertThat(chatCompletionStreamOptions.includeUsage()).contains(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionStreamOptions = + ChatCompletionStreamOptions.builder().includeUsage(true).build() + + val roundtrippedChatCompletionStreamOptions = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionStreamOptions), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionStreamOptions).isEqualTo(chatCompletionStreamOptions) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParamTest.kt index 1263d59a3..c0a8f1e7e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionSystemMessageParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,20 @@ internal class ChatCompletionSystemMessageParamTest { .isEqualTo(ChatCompletionSystemMessageParam.Content.ofText("string")) assertThat(chatCompletionSystemMessageParam.name()).contains("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionSystemMessageParam = + ChatCompletionSystemMessageParam.builder().content("string").name("name").build() + + val roundtrippedChatCompletionSystemMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionSystemMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionSystemMessageParam) + .isEqualTo(chatCompletionSystemMessageParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTest.kt index 11fb13176..51cfc3d22 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.completions.CompletionUsage import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -231,4 +233,127 @@ internal class ChatCompletionTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletion = + ChatCompletion.builder() + .id("id") + .addChoice( + ChatCompletion.Choice.builder() + .finishReason(ChatCompletion.Choice.FinishReason.STOP) + .index(0L) + .logprobs( + ChatCompletion.Choice.Logprobs.builder() + .addContent( + ChatCompletionTokenLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .addTopLogprob( + ChatCompletionTokenLogprob.TopLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .build() + ) + .build() + ) + .addRefusal( + ChatCompletionTokenLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .addTopLogprob( + ChatCompletionTokenLogprob.TopLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .build() + ) + .build() + ) + .build() + ) + .message( + ChatCompletionMessage.builder() + .content("content") + .refusal("refusal") + .addAnnotation( + ChatCompletionMessage.Annotation.builder() + .urlCitation( + ChatCompletionMessage.Annotation.UrlCitation.builder() + .endIndex(0L) + .startIndex(0L) + .title("title") + .url("url") + .build() + ) + .build() + ) + .audio( + ChatCompletionAudio.builder() + .id("id") + .data("data") + .expiresAt(0L) + .transcript("transcript") + .build() + ) + .functionCall( + ChatCompletionMessage.FunctionCall.builder() + .arguments("arguments") + .name("name") + .build() + ) + .addToolCall( + ChatCompletionMessageToolCall.builder() + .id("id") + .function( + ChatCompletionMessageToolCall.Function.builder() + .arguments("arguments") + .name("name") + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .created(0L) + .model("model") + .serviceTier(ChatCompletion.ServiceTier.SCALE) + .systemFingerprint("system_fingerprint") + .usage( + CompletionUsage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .completionTokensDetails( + CompletionUsage.CompletionTokensDetails.builder() + .acceptedPredictionTokens(0L) + .audioTokens(0L) + .reasoningTokens(0L) + .rejectedPredictionTokens(0L) + .build() + ) + .promptTokensDetails( + CompletionUsage.PromptTokensDetails.builder() + .audioTokens(0L) + .cachedTokens(0L) + .build() + ) + .build() + ) + .build() + + val roundtrippedChatCompletion = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletion), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletion).isEqualTo(chatCompletion) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprobTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprobTest.kt index 2ecb51e42..6fc961221 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprobTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionTokenLogprobTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -36,4 +38,30 @@ internal class ChatCompletionTokenLogprobTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionTokenLogprob = + ChatCompletionTokenLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .addTopLogprob( + ChatCompletionTokenLogprob.TopLogprob.builder() + .token("token") + .addByte(0L) + .logprob(0.0) + .build() + ) + .build() + + val roundtrippedChatCompletionTokenLogprob = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionTokenLogprob), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionTokenLogprob).isEqualTo(chatCompletionTokenLogprob) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOptionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOptionTest.kt index 5e4833687..78ee6f41b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOptionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolChoiceOptionTest.kt @@ -2,8 +2,13 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows internal class ChatCompletionToolChoiceOptionTest { @@ -17,6 +22,22 @@ internal class ChatCompletionToolChoiceOptionTest { assertThat(chatCompletionToolChoiceOption.namedToolChoice()).isEmpty } + @Test + fun ofAutoRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionToolChoiceOption = + ChatCompletionToolChoiceOption.ofAuto(ChatCompletionToolChoiceOption.Auto.NONE) + + val roundtrippedChatCompletionToolChoiceOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionToolChoiceOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionToolChoiceOption) + .isEqualTo(chatCompletionToolChoiceOption) + } + @Test fun ofNamedToolChoice() { val namedToolChoice = @@ -30,4 +51,35 @@ internal class ChatCompletionToolChoiceOptionTest { assertThat(chatCompletionToolChoiceOption.auto()).isEmpty assertThat(chatCompletionToolChoiceOption.namedToolChoice()).contains(namedToolChoice) } + + @Test + fun ofNamedToolChoiceRoundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionToolChoiceOption = + ChatCompletionToolChoiceOption.ofNamedToolChoice( + ChatCompletionNamedToolChoice.builder() + .function(ChatCompletionNamedToolChoice.Function.builder().name("name").build()) + .build() + ) + + val roundtrippedChatCompletionToolChoiceOption = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionToolChoiceOption), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionToolChoiceOption) + .isEqualTo(chatCompletionToolChoiceOption) + } + + @Test + fun incompatibleJsonShapeDeserializesToUnknown() { + val value = JsonValue.from(listOf("invalid", "array")) + val chatCompletionToolChoiceOption = + jsonMapper().convertValue(value, jacksonTypeRef()) + + val e = + assertThrows { chatCompletionToolChoiceOption.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParamTest.kt index ec152dc6a..007f7b458 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolMessageParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -19,4 +21,23 @@ internal class ChatCompletionToolMessageParamTest { .isEqualTo(ChatCompletionToolMessageParam.Content.ofText("string")) assertThat(chatCompletionToolMessageParam.toolCallId()).isEqualTo("tool_call_id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionToolMessageParam = + ChatCompletionToolMessageParam.builder() + .content("string") + .toolCallId("tool_call_id") + .build() + + val roundtrippedChatCompletionToolMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionToolMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionToolMessageParam) + .isEqualTo(chatCompletionToolMessageParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolTest.kt index 0348ce937..485362da0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionToolTest.kt @@ -2,7 +2,9 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.FunctionDefinition import com.openai.models.FunctionParameters import org.assertj.core.api.Assertions.assertThat @@ -42,4 +44,32 @@ internal class ChatCompletionToolTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionTool = + ChatCompletionTool.builder() + .function( + FunctionDefinition.builder() + .name("name") + .description("description") + .parameters( + FunctionParameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .build() + ) + .build() + + val roundtrippedChatCompletionTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionTool).isEqualTo(chatCompletionTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParamTest.kt index a15e8c8cd..2027b08bd 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionUserMessageParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,20 @@ internal class ChatCompletionUserMessageParamTest { .isEqualTo(ChatCompletionUserMessageParam.Content.ofText("string")) assertThat(chatCompletionUserMessageParam.name()).contains("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val chatCompletionUserMessageParam = + ChatCompletionUserMessageParam.builder().content("string").name("name").build() + + val roundtrippedChatCompletionUserMessageParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(chatCompletionUserMessageParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedChatCompletionUserMessageParam) + .isEqualTo(chatCompletionUserMessageParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionChoiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionChoiceTest.kt index ab3f1b689..ada0854b4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionChoiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionChoiceTest.kt @@ -2,7 +2,9 @@ package com.openai.models.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -46,4 +48,35 @@ internal class CompletionChoiceTest { ) assertThat(completionChoice.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val completionChoice = + CompletionChoice.builder() + .finishReason(CompletionChoice.FinishReason.STOP) + .index(0L) + .logprobs( + CompletionChoice.Logprobs.builder() + .addTextOffset(0L) + .addTokenLogprob(0.0) + .addToken("string") + .addTopLogprob( + CompletionChoice.Logprobs.TopLogprob.builder() + .putAdditionalProperty("foo", JsonValue.from(0)) + .build() + ) + .build() + ) + .text("text") + .build() + + val roundtrippedCompletionChoice = + jsonMapper.readValue( + jsonMapper.writeValueAsString(completionChoice), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCompletionChoice).isEqualTo(completionChoice) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionTest.kt index 87f07d127..880f395f4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionTest.kt @@ -2,7 +2,9 @@ package com.openai.models.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -105,4 +107,64 @@ internal class CompletionTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val completion = + Completion.builder() + .id("id") + .addChoice( + CompletionChoice.builder() + .finishReason(CompletionChoice.FinishReason.STOP) + .index(0L) + .logprobs( + CompletionChoice.Logprobs.builder() + .addTextOffset(0L) + .addTokenLogprob(0.0) + .addToken("string") + .addTopLogprob( + CompletionChoice.Logprobs.TopLogprob.builder() + .putAdditionalProperty("foo", JsonValue.from(0)) + .build() + ) + .build() + ) + .text("text") + .build() + ) + .created(0L) + .model("model") + .systemFingerprint("system_fingerprint") + .usage( + CompletionUsage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .completionTokensDetails( + CompletionUsage.CompletionTokensDetails.builder() + .acceptedPredictionTokens(0L) + .audioTokens(0L) + .reasoningTokens(0L) + .rejectedPredictionTokens(0L) + .build() + ) + .promptTokensDetails( + CompletionUsage.PromptTokensDetails.builder() + .audioTokens(0L) + .cachedTokens(0L) + .build() + ) + .build() + ) + .build() + + val roundtrippedCompletion = + jsonMapper.readValue( + jsonMapper.writeValueAsString(completion), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCompletion).isEqualTo(completion) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionUsageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionUsageTest.kt index 3e044d98d..9068bea23 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionUsageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/completions/CompletionUsageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.completions +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -50,4 +52,37 @@ internal class CompletionUsageTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val completionUsage = + CompletionUsage.builder() + .completionTokens(0L) + .promptTokens(0L) + .totalTokens(0L) + .completionTokensDetails( + CompletionUsage.CompletionTokensDetails.builder() + .acceptedPredictionTokens(0L) + .audioTokens(0L) + .reasoningTokens(0L) + .rejectedPredictionTokens(0L) + .build() + ) + .promptTokensDetails( + CompletionUsage.PromptTokensDetails.builder() + .audioTokens(0L) + .cachedTokens(0L) + .build() + ) + .build() + + val roundtrippedCompletionUsage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(completionUsage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCompletionUsage).isEqualTo(completionUsage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/embeddings/CreateEmbeddingResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/embeddings/CreateEmbeddingResponseTest.kt index c4dd57b76..15f386dc8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/embeddings/CreateEmbeddingResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/embeddings/CreateEmbeddingResponseTest.kt @@ -2,6 +2,8 @@ package com.openai.models.embeddings +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -26,4 +28,25 @@ internal class CreateEmbeddingResponseTest { CreateEmbeddingResponse.Usage.builder().promptTokens(0L).totalTokens(0L).build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val createEmbeddingResponse = + CreateEmbeddingResponse.builder() + .addData(Embedding.builder().addEmbedding(0.0).index(0L).build()) + .model("model") + .usage( + CreateEmbeddingResponse.Usage.builder().promptTokens(0L).totalTokens(0L).build() + ) + .build() + + val roundtrippedCreateEmbeddingResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(createEmbeddingResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCreateEmbeddingResponse).isEqualTo(createEmbeddingResponse) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/embeddings/EmbeddingTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/embeddings/EmbeddingTest.kt index 998d38ca5..7f29107e9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/embeddings/EmbeddingTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/embeddings/EmbeddingTest.kt @@ -2,6 +2,8 @@ package com.openai.models.embeddings +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class EmbeddingTest { assertThat(embedding.embedding()).containsExactly(0.0) assertThat(embedding.index()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val embedding = Embedding.builder().addEmbedding(0.0).index(0L).build() + + val roundtrippedEmbedding = + jsonMapper.readValue( + jsonMapper.writeValueAsString(embedding), + jacksonTypeRef(), + ) + + assertThat(roundtrippedEmbedding).isEqualTo(embedding) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/files/FileDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/files/FileDeletedTest.kt index ab30de6a8..399759d98 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/files/FileDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/files/FileDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.files +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class FileDeletedTest { assertThat(fileDeleted.id()).isEqualTo("id") assertThat(fileDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileDeleted = FileDeleted.builder().id("id").deleted(true).build() + + val roundtrippedFileDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileDeleted).isEqualTo(fileDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/files/FileObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/files/FileObjectTest.kt index 9a4f158b1..fbd36c4de 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/files/FileObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/files/FileObjectTest.kt @@ -2,6 +2,8 @@ package com.openai.models.files +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -30,4 +32,28 @@ internal class FileObjectTest { assertThat(fileObject.expiresAt()).contains(0L) assertThat(fileObject.statusDetails()).contains("status_details") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileObject = + FileObject.builder() + .id("id") + .bytes(0L) + .createdAt(0L) + .filename("filename") + .purpose(FileObject.Purpose.ASSISTANTS) + .status(FileObject.Status.UPLOADED) + .expiresAt(0L) + .statusDetails("status_details") + .build() + + val roundtrippedFileObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileObject).isEqualTo(fileObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEventTest.kt index be5015458..336f21824 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.finetuning.jobs +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -27,4 +29,26 @@ internal class FineTuningJobEventTest { assertThat(fineTuningJobEvent._data()).isEqualTo(JsonValue.from(mapOf())) assertThat(fineTuningJobEvent.type()).contains(FineTuningJobEvent.Type.MESSAGE) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fineTuningJobEvent = + FineTuningJobEvent.builder() + .id("id") + .createdAt(0L) + .level(FineTuningJobEvent.Level.INFO) + .message("message") + .data(JsonValue.from(mapOf())) + .type(FineTuningJobEvent.Type.MESSAGE) + .build() + + val roundtrippedFineTuningJobEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fineTuningJobEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFineTuningJobEvent).isEqualTo(fineTuningJobEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt index 3860b689c..33c530e68 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt @@ -2,7 +2,9 @@ package com.openai.models.finetuning.jobs +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -161,4 +163,92 @@ internal class FineTuningJobTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fineTuningJob = + FineTuningJob.builder() + .id("id") + .createdAt(0L) + .error( + FineTuningJob.Error.builder() + .code("code") + .message("message") + .param("param") + .build() + ) + .fineTunedModel("fine_tuned_model") + .finishedAt(0L) + .hyperparameters( + FineTuningJob.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .model("model") + .organizationId("organization_id") + .addResultFile("file-abc123") + .seed(0L) + .status(FineTuningJob.Status.VALIDATING_FILES) + .trainedTokens(0L) + .trainingFile("training_file") + .validationFile("validation_file") + .estimatedFinish(0L) + .addIntegration( + FineTuningJobWandbIntegrationObject.builder() + .wandb( + FineTuningJobWandbIntegration.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") + .build() + ) + .build() + ) + .metadata( + FineTuningJob.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .method( + FineTuningJob.Method.builder() + .dpo( + FineTuningJob.Method.Dpo.builder() + .hyperparameters( + FineTuningJob.Method.Dpo.Hyperparameters.builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .supervised( + FineTuningJob.Method.Supervised.builder() + .hyperparameters( + FineTuningJob.Method.Supervised.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .type(FineTuningJob.Method.Type.SUPERVISED) + .build() + ) + .build() + + val roundtrippedFineTuningJob = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fineTuningJob), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFineTuningJob).isEqualTo(fineTuningJob) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObjectTest.kt index 079913fc1..b46ff3521 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationObjectTest.kt @@ -2,6 +2,8 @@ package com.openai.models.finetuning.jobs +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -31,4 +33,29 @@ internal class FineTuningJobWandbIntegrationObjectTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fineTuningJobWandbIntegrationObject = + FineTuningJobWandbIntegrationObject.builder() + .wandb( + FineTuningJobWandbIntegration.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") + .build() + ) + .build() + + val roundtrippedFineTuningJobWandbIntegrationObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fineTuningJobWandbIntegrationObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFineTuningJobWandbIntegrationObject) + .isEqualTo(fineTuningJobWandbIntegrationObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationTest.kt index ae4d42d9e..c3cac9272 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobWandbIntegrationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.finetuning.jobs +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -23,4 +25,25 @@ internal class FineTuningJobWandbIntegrationTest { assertThat(fineTuningJobWandbIntegration.name()).contains("name") assertThat(fineTuningJobWandbIntegration.tags().getOrNull()).containsExactly("custom-tag") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fineTuningJobWandbIntegration = + FineTuningJobWandbIntegration.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") + .build() + + val roundtrippedFineTuningJobWandbIntegration = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fineTuningJobWandbIntegration), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFineTuningJobWandbIntegration) + .isEqualTo(fineTuningJobWandbIntegration) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpointTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpointTest.kt index c17eb54ea..9e13e45fe 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpointTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/checkpoints/FineTuningJobCheckpointTest.kt @@ -2,6 +2,8 @@ package com.openai.models.finetuning.jobs.checkpoints +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -48,4 +50,36 @@ internal class FineTuningJobCheckpointTest { ) assertThat(fineTuningJobCheckpoint.stepNumber()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fineTuningJobCheckpoint = + FineTuningJobCheckpoint.builder() + .id("id") + .createdAt(0L) + .fineTunedModelCheckpoint("fine_tuned_model_checkpoint") + .fineTuningJobId("fine_tuning_job_id") + .metrics( + FineTuningJobCheckpoint.Metrics.builder() + .fullValidLoss(0.0) + .fullValidMeanTokenAccuracy(0.0) + .step(0.0) + .trainLoss(0.0) + .trainMeanTokenAccuracy(0.0) + .validLoss(0.0) + .validMeanTokenAccuracy(0.0) + .build() + ) + .stepNumber(0L) + .build() + + val roundtrippedFineTuningJobCheckpoint = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fineTuningJobCheckpoint), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFineTuningJobCheckpoint).isEqualTo(fineTuningJobCheckpoint) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/images/ImageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/images/ImageTest.kt index e14634176..0313b1b9e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/images/ImageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/images/ImageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.images +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,16 @@ internal class ImageTest { assertThat(image.revisedPrompt()).contains("revised_prompt") assertThat(image.url()).contains("url") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val image = + Image.builder().b64Json("b64_json").revisedPrompt("revised_prompt").url("url").build() + + val roundtrippedImage = + jsonMapper.readValue(jsonMapper.writeValueAsString(image), jacksonTypeRef()) + + assertThat(roundtrippedImage).isEqualTo(image) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/images/ImagesResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/images/ImagesResponseTest.kt index 6c6185874..7c13153ff 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/images/ImagesResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/images/ImagesResponseTest.kt @@ -2,6 +2,8 @@ package com.openai.models.images +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -31,4 +33,28 @@ internal class ImagesResponseTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val imagesResponse = + ImagesResponse.builder() + .created(0L) + .addData( + Image.builder() + .b64Json("b64_json") + .revisedPrompt("revised_prompt") + .url("url") + .build() + ) + .build() + + val roundtrippedImagesResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(imagesResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedImagesResponse).isEqualTo(imagesResponse) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/models/ModelDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/models/ModelDeletedTest.kt index a71cf745f..5836f8079 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/models/ModelDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/models/ModelDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,18 @@ internal class ModelDeletedTest { assertThat(modelDeleted.deleted()).isEqualTo(true) assertThat(modelDeleted.object_()).isEqualTo("object") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val modelDeleted = ModelDeleted.builder().id("id").deleted(true).object_("object").build() + + val roundtrippedModelDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(modelDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModelDeleted).isEqualTo(modelDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/models/ModelTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/models/ModelTest.kt index 8fc55a6e2..456d9fa9a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/models/ModelTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/models/ModelTest.kt @@ -2,6 +2,8 @@ package com.openai.models.models +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,15 @@ internal class ModelTest { assertThat(model.created()).isEqualTo(0L) assertThat(model.ownedBy()).isEqualTo("owned_by") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val model = Model.builder().id("id").created(0L).ownedBy("owned_by").build() + + val roundtrippedModel = + jsonMapper.readValue(jsonMapper.writeValueAsString(model), jacksonTypeRef()) + + assertThat(roundtrippedModel).isEqualTo(model) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationCreateResponseTest.kt index 659788c65..903824ac2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationCreateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationCreateResponseTest.kt @@ -2,6 +2,8 @@ package com.openai.models.moderations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -157,4 +159,92 @@ internal class ModerationCreateResponseTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val moderationCreateResponse = + ModerationCreateResponse.builder() + .id("id") + .model("model") + .addResult( + Moderation.builder() + .categories( + Moderation.Categories.builder() + .harassment(true) + .harassmentThreatening(true) + .hate(true) + .hateThreatening(true) + .illicit(true) + .illicitViolent(true) + .selfHarm(true) + .selfHarmInstructions(true) + .selfHarmIntent(true) + .sexual(true) + .sexualMinors(true) + .violence(true) + .violenceGraphic(true) + .build() + ) + .categoryAppliedInputTypes( + Moderation.CategoryAppliedInputTypes.builder() + .addHarassment(Moderation.CategoryAppliedInputTypes.Harassment.TEXT) + .addHarassmentThreatening( + Moderation.CategoryAppliedInputTypes.HarassmentThreatening.TEXT + ) + .addHate(Moderation.CategoryAppliedInputTypes.Hate.TEXT) + .addHateThreatening( + Moderation.CategoryAppliedInputTypes.HateThreatening.TEXT + ) + .addIllicit(Moderation.CategoryAppliedInputTypes.Illicit.TEXT) + .addIllicitViolent( + Moderation.CategoryAppliedInputTypes.IllicitViolent.TEXT + ) + .addSelfHarm(Moderation.CategoryAppliedInputTypes.SelfHarm.TEXT) + .addSelfHarmInstruction( + Moderation.CategoryAppliedInputTypes.SelfHarmInstruction.TEXT + ) + .addSelfHarmIntent( + Moderation.CategoryAppliedInputTypes.SelfHarmIntent.TEXT + ) + .addSexual(Moderation.CategoryAppliedInputTypes.Sexual.TEXT) + .addSexualMinor( + Moderation.CategoryAppliedInputTypes.SexualMinor.TEXT + ) + .addViolence(Moderation.CategoryAppliedInputTypes.Violence.TEXT) + .addViolenceGraphic( + Moderation.CategoryAppliedInputTypes.ViolenceGraphic.TEXT + ) + .build() + ) + .categoryScores( + Moderation.CategoryScores.builder() + .harassment(0.0) + .harassmentThreatening(0.0) + .hate(0.0) + .hateThreatening(0.0) + .illicit(0.0) + .illicitViolent(0.0) + .selfHarm(0.0) + .selfHarmInstructions(0.0) + .selfHarmIntent(0.0) + .sexual(0.0) + .sexualMinors(0.0) + .violence(0.0) + .violenceGraphic(0.0) + .build() + ) + .flagged(true) + .build() + ) + .build() + + val roundtrippedModerationCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(moderationCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModerationCreateResponse).isEqualTo(moderationCreateResponse) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationImageUrlInputTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationImageUrlInputTest.kt index 1674c51c3..1398c6ab6 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationImageUrlInputTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationImageUrlInputTest.kt @@ -2,6 +2,8 @@ package com.openai.models.moderations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -25,4 +27,25 @@ internal class ModerationImageUrlInputTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val moderationImageUrlInput = + ModerationImageUrlInput.builder() + .imageUrl( + ModerationImageUrlInput.ImageUrl.builder() + .url("https://example.com/image.jpg") + .build() + ) + .build() + + val roundtrippedModerationImageUrlInput = + jsonMapper.readValue( + jsonMapper.writeValueAsString(moderationImageUrlInput), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModerationImageUrlInput).isEqualTo(moderationImageUrlInput) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationMultiModalInputTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationMultiModalInputTest.kt index ee066a492..b47b114f2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationMultiModalInputTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationMultiModalInputTest.kt @@ -2,8 +2,15 @@ package com.openai.models.moderations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ModerationMultiModalInputTest { @@ -24,6 +31,29 @@ internal class ModerationMultiModalInputTest { assertThat(moderationMultiModalInput.text()).isEmpty } + @Test + fun ofImageUrlRoundtrip() { + val jsonMapper = jsonMapper() + val moderationMultiModalInput = + ModerationMultiModalInput.ofImageUrl( + ModerationImageUrlInput.builder() + .imageUrl( + ModerationImageUrlInput.ImageUrl.builder() + .url("https://example.com/image.jpg") + .build() + ) + .build() + ) + + val roundtrippedModerationMultiModalInput = + jsonMapper.readValue( + jsonMapper.writeValueAsString(moderationMultiModalInput), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModerationMultiModalInput).isEqualTo(moderationMultiModalInput) + } + @Test fun ofText() { val text = ModerationTextInput.builder().text("I want to kill them").build() @@ -33,4 +63,39 @@ internal class ModerationMultiModalInputTest { assertThat(moderationMultiModalInput.imageUrl()).isEmpty assertThat(moderationMultiModalInput.text()).contains(text) } + + @Test + fun ofTextRoundtrip() { + val jsonMapper = jsonMapper() + val moderationMultiModalInput = + ModerationMultiModalInput.ofText( + ModerationTextInput.builder().text("I want to kill them").build() + ) + + val roundtrippedModerationMultiModalInput = + jsonMapper.readValue( + jsonMapper.writeValueAsString(moderationMultiModalInput), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModerationMultiModalInput).isEqualTo(moderationMultiModalInput) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val moderationMultiModalInput = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { moderationMultiModalInput.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTest.kt index fbe8c22d2..ec9f85c0b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTest.kt @@ -2,6 +2,8 @@ package com.openai.models.moderations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -133,4 +135,80 @@ internal class ModerationTest { ) assertThat(moderation.flagged()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val moderation = + Moderation.builder() + .categories( + Moderation.Categories.builder() + .harassment(true) + .harassmentThreatening(true) + .hate(true) + .hateThreatening(true) + .illicit(true) + .illicitViolent(true) + .selfHarm(true) + .selfHarmInstructions(true) + .selfHarmIntent(true) + .sexual(true) + .sexualMinors(true) + .violence(true) + .violenceGraphic(true) + .build() + ) + .categoryAppliedInputTypes( + Moderation.CategoryAppliedInputTypes.builder() + .addHarassment(Moderation.CategoryAppliedInputTypes.Harassment.TEXT) + .addHarassmentThreatening( + Moderation.CategoryAppliedInputTypes.HarassmentThreatening.TEXT + ) + .addHate(Moderation.CategoryAppliedInputTypes.Hate.TEXT) + .addHateThreatening( + Moderation.CategoryAppliedInputTypes.HateThreatening.TEXT + ) + .addIllicit(Moderation.CategoryAppliedInputTypes.Illicit.TEXT) + .addIllicitViolent(Moderation.CategoryAppliedInputTypes.IllicitViolent.TEXT) + .addSelfHarm(Moderation.CategoryAppliedInputTypes.SelfHarm.TEXT) + .addSelfHarmInstruction( + Moderation.CategoryAppliedInputTypes.SelfHarmInstruction.TEXT + ) + .addSelfHarmIntent(Moderation.CategoryAppliedInputTypes.SelfHarmIntent.TEXT) + .addSexual(Moderation.CategoryAppliedInputTypes.Sexual.TEXT) + .addSexualMinor(Moderation.CategoryAppliedInputTypes.SexualMinor.TEXT) + .addViolence(Moderation.CategoryAppliedInputTypes.Violence.TEXT) + .addViolenceGraphic( + Moderation.CategoryAppliedInputTypes.ViolenceGraphic.TEXT + ) + .build() + ) + .categoryScores( + Moderation.CategoryScores.builder() + .harassment(0.0) + .harassmentThreatening(0.0) + .hate(0.0) + .hateThreatening(0.0) + .illicit(0.0) + .illicitViolent(0.0) + .selfHarm(0.0) + .selfHarmInstructions(0.0) + .selfHarmIntent(0.0) + .sexual(0.0) + .sexualMinors(0.0) + .violence(0.0) + .violenceGraphic(0.0) + .build() + ) + .flagged(true) + .build() + + val roundtrippedModeration = + jsonMapper.readValue( + jsonMapper.writeValueAsString(moderation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModeration).isEqualTo(moderation) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTextInputTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTextInputTest.kt index 4d4ceabd1..cde1b8d55 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTextInputTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/moderations/ModerationTextInputTest.kt @@ -2,6 +2,8 @@ package com.openai.models.moderations +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class ModerationTextInputTest { assertThat(moderationTextInput.text()).isEqualTo("I want to kill them") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val moderationTextInput = ModerationTextInput.builder().text("I want to kill them").build() + + val roundtrippedModerationTextInput = + jsonMapper.readValue( + jsonMapper.writeValueAsString(moderationTextInput), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModerationTextInput).isEqualTo(moderationTextInput) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt index 0f5bf38e2..be2fdaa67 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ComputerToolTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -20,4 +22,23 @@ internal class ComputerToolTest { assertThat(computerTool.displayWidth()).isEqualTo(0.0) assertThat(computerTool.environment()).isEqualTo(ComputerTool.Environment.MAC) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val computerTool = + ComputerTool.builder() + .displayHeight(0.0) + .displayWidth(0.0) + .environment(ComputerTool.Environment.MAC) + .build() + + val roundtrippedComputerTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(computerTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedComputerTool).isEqualTo(computerTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/EasyInputMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/EasyInputMessageTest.kt index 5a04b2398..7e5d6f364 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/EasyInputMessageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/EasyInputMessageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -21,4 +23,23 @@ internal class EasyInputMessageTest { assertThat(easyInputMessage.role()).isEqualTo(EasyInputMessage.Role.USER) assertThat(easyInputMessage.type()).contains(EasyInputMessage.Type.MESSAGE) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val easyInputMessage = + EasyInputMessage.builder() + .content("string") + .role(EasyInputMessage.Role.USER) + .type(EasyInputMessage.Type.MESSAGE) + .build() + + val roundtrippedEasyInputMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(easyInputMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedEasyInputMessage).isEqualTo(easyInputMessage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/FileSearchToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/FileSearchToolTest.kt index ae71a3ff4..17ec29880 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/FileSearchToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/FileSearchToolTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.ComparisonFilter import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -49,4 +51,35 @@ internal class FileSearchToolTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileSearchTool = + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + + val roundtrippedFileSearchTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileSearchTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileSearchTool).isEqualTo(fileSearchTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt index d3fb35fb7..0769309b0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/FunctionToolTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -32,4 +34,28 @@ internal class FunctionToolTest { assertThat(functionTool.strict()).isEqualTo(true) assertThat(functionTool.description()).contains("description") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val functionTool = + FunctionTool.builder() + .name("name") + .parameters( + FunctionTool.Parameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .description("description") + .build() + + val roundtrippedFunctionTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(functionTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFunctionTool).isEqualTo(functionTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDeltaEventTest.kt index 47822272f..1c7bf17d8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class ResponseAudioDeltaEventTest { assertThat(responseAudioDeltaEvent.delta()).isEqualTo("delta") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseAudioDeltaEvent = ResponseAudioDeltaEvent.builder().delta("delta").build() + + val roundtrippedResponseAudioDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseAudioDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseAudioDeltaEvent).isEqualTo(responseAudioDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDoneEventTest.kt index 61ff55ff8..241dc9468 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioDoneEventTest.kt @@ -2,6 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class ResponseAudioDoneEventTest { @@ -10,4 +13,18 @@ internal class ResponseAudioDoneEventTest { fun create() { val responseAudioDoneEvent = ResponseAudioDoneEvent.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseAudioDoneEvent = ResponseAudioDoneEvent.builder().build() + + val roundtrippedResponseAudioDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseAudioDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseAudioDoneEvent).isEqualTo(responseAudioDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEventTest.kt index 2b86a5c80..f1a3c902e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,20 @@ internal class ResponseAudioTranscriptDeltaEventTest { assertThat(responseAudioTranscriptDeltaEvent.delta()).isEqualTo("delta") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseAudioTranscriptDeltaEvent = + ResponseAudioTranscriptDeltaEvent.builder().delta("delta").build() + + val roundtrippedResponseAudioTranscriptDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseAudioTranscriptDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseAudioTranscriptDeltaEvent) + .isEqualTo(responseAudioTranscriptDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEventTest.kt index 9395cbd44..29285e707 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseAudioTranscriptDoneEventTest.kt @@ -2,6 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class ResponseAudioTranscriptDoneEventTest { @@ -10,4 +13,19 @@ internal class ResponseAudioTranscriptDoneEventTest { fun create() { val responseAudioTranscriptDoneEvent = ResponseAudioTranscriptDoneEvent.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseAudioTranscriptDoneEvent = ResponseAudioTranscriptDoneEvent.builder().build() + + val roundtrippedResponseAudioTranscriptDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseAudioTranscriptDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseAudioTranscriptDoneEvent) + .isEqualTo(responseAudioTranscriptDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEventTest.kt index 653ea1a91..eaf3059b8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,4 +20,23 @@ internal class ResponseCodeInterpreterCallCodeDeltaEventTest { assertThat(responseCodeInterpreterCallCodeDeltaEvent.delta()).isEqualTo("delta") assertThat(responseCodeInterpreterCallCodeDeltaEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCodeInterpreterCallCodeDeltaEvent = + ResponseCodeInterpreterCallCodeDeltaEvent.builder() + .delta("delta") + .outputIndex(0L) + .build() + + val roundtrippedResponseCodeInterpreterCallCodeDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCodeInterpreterCallCodeDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCodeInterpreterCallCodeDeltaEvent) + .isEqualTo(responseCodeInterpreterCallCodeDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEventTest.kt index 35d1d6998..1e4657127 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCodeDoneEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ResponseCodeInterpreterCallCodeDoneEventTest { assertThat(responseCodeInterpreterCallCodeDoneEvent.code()).isEqualTo("code") assertThat(responseCodeInterpreterCallCodeDoneEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCodeInterpreterCallCodeDoneEvent = + ResponseCodeInterpreterCallCodeDoneEvent.builder().code("code").outputIndex(0L).build() + + val roundtrippedResponseCodeInterpreterCallCodeDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCodeInterpreterCallCodeDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCodeInterpreterCallCodeDoneEvent) + .isEqualTo(responseCodeInterpreterCallCodeDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEventTest.kt index 57728546b..e6b8fa5f0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallCompletedEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,30 @@ internal class ResponseCodeInterpreterCallCompletedEventTest { ) assertThat(responseCodeInterpreterCallCompletedEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCodeInterpreterCallCompletedEvent = + ResponseCodeInterpreterCallCompletedEvent.builder() + .codeInterpreterCall( + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + + val roundtrippedResponseCodeInterpreterCallCompletedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCodeInterpreterCallCompletedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCodeInterpreterCallCompletedEvent) + .isEqualTo(responseCodeInterpreterCallCompletedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEventTest.kt index 4d2ed35dc..ef31a27dd 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInProgressEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,30 @@ internal class ResponseCodeInterpreterCallInProgressEventTest { ) assertThat(responseCodeInterpreterCallInProgressEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCodeInterpreterCallInProgressEvent = + ResponseCodeInterpreterCallInProgressEvent.builder() + .codeInterpreterCall( + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + + val roundtrippedResponseCodeInterpreterCallInProgressEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCodeInterpreterCallInProgressEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCodeInterpreterCallInProgressEvent) + .isEqualTo(responseCodeInterpreterCallInProgressEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEventTest.kt index df27ea081..446078e95 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterCallInterpretingEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,4 +35,30 @@ internal class ResponseCodeInterpreterCallInterpretingEventTest { ) assertThat(responseCodeInterpreterCallInterpretingEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCodeInterpreterCallInterpretingEvent = + ResponseCodeInterpreterCallInterpretingEvent.builder() + .codeInterpreterCall( + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + + val roundtrippedResponseCodeInterpreterCallInterpretingEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCodeInterpreterCallInterpretingEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCodeInterpreterCallInterpretingEvent) + .isEqualTo(responseCodeInterpreterCallInterpretingEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCallTest.kt index b135049fb..3ec019cf0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCodeInterpreterToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,4 +30,25 @@ internal class ResponseCodeInterpreterToolCallTest { assertThat(responseCodeInterpreterToolCall.status()) .isEqualTo(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCodeInterpreterToolCall = + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseCodeInterpreterToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCodeInterpreterToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCodeInterpreterToolCall) + .isEqualTo(responseCodeInterpreterToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCompletedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCompletedEventTest.kt index e7bd30a40..31fa0db82 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCompletedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCompletedEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.ChatModel import com.openai.models.ComparisonFilter import com.openai.models.Reasoning @@ -213,4 +215,117 @@ internal class ResponseCompletedEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCompletedEvent = + ResponseCompletedEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + + val roundtrippedResponseCompletedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCompletedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCompletedEvent).isEqualTo(responseCompletedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItemTest.kt index d89bd8cab..59ae45722 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputItemTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -50,4 +52,37 @@ internal class ResponseComputerToolCallOutputItemTest { assertThat(responseComputerToolCallOutputItem.status()) .contains(ResponseComputerToolCallOutputItem.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseComputerToolCallOutputItem = + ResponseComputerToolCallOutputItem.builder() + .id("id") + .callId("call_id") + .output( + ResponseComputerToolCallOutputScreenshot.builder() + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + .addAcknowledgedSafetyCheck( + ResponseComputerToolCallOutputItem.AcknowledgedSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCallOutputItem.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseComputerToolCallOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseComputerToolCallOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseComputerToolCallOutputItem) + .isEqualTo(responseComputerToolCallOutputItem) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshotTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshotTest.kt index 43e247abf..54cb39061 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshotTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallOutputScreenshotTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,4 +20,23 @@ internal class ResponseComputerToolCallOutputScreenshotTest { assertThat(responseComputerToolCallOutputScreenshot.fileId()).contains("file_id") assertThat(responseComputerToolCallOutputScreenshot.imageUrl()).contains("image_url") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseComputerToolCallOutputScreenshot = + ResponseComputerToolCallOutputScreenshot.builder() + .fileId("file_id") + .imageUrl("image_url") + .build() + + val roundtrippedResponseComputerToolCallOutputScreenshot = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseComputerToolCallOutputScreenshot), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseComputerToolCallOutputScreenshot) + .isEqualTo(responseComputerToolCallOutputScreenshot) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallTest.kt index d1eebfd70..4021e38ea 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseComputerToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -56,4 +58,38 @@ internal class ResponseComputerToolCallTest { assertThat(responseComputerToolCall.type()) .isEqualTo(ResponseComputerToolCall.Type.COMPUTER_CALL) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseComputerToolCall = + ResponseComputerToolCall.builder() + .id("id") + .action( + ResponseComputerToolCall.Action.Click.builder() + .button(ResponseComputerToolCall.Action.Click.Button.LEFT) + .x(0L) + .y(0L) + .build() + ) + .callId("call_id") + .addPendingSafetyCheck( + ResponseComputerToolCall.PendingSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCall.Status.IN_PROGRESS) + .type(ResponseComputerToolCall.Type.COMPUTER_CALL) + .build() + + val roundtrippedResponseComputerToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseComputerToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseComputerToolCall).isEqualTo(responseComputerToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartAddedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartAddedEventTest.kt index d72094d3f..07ea2d70b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartAddedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartAddedEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -45,4 +47,35 @@ internal class ResponseContentPartAddedEventTest { ) ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseContentPartAddedEvent = + ResponseContentPartAddedEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .part( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .build() + + val roundtrippedResponseContentPartAddedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContentPartAddedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContentPartAddedEvent) + .isEqualTo(responseContentPartAddedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartDoneEventTest.kt index 7f6fb51da..deb7f0846 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentPartDoneEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -45,4 +47,34 @@ internal class ResponseContentPartDoneEventTest { ) ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseContentPartDoneEvent = + ResponseContentPartDoneEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .part( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .build() + + val roundtrippedResponseContentPartDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContentPartDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContentPartDoneEvent).isEqualTo(responseContentPartDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt index fea5be78f..062f34e7e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseContentTest.kt @@ -2,8 +2,15 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseContentTest { @@ -20,9 +27,29 @@ internal class ResponseContentTest { assertThat(responseContent.outputRefusal()).isEmpty } + @Test + fun ofInputTextRoundtrip() { + val jsonMapper = jsonMapper() + val responseContent = + ResponseContent.ofInputText(ResponseInputText.builder().text("text").build()) + + val roundtrippedResponseContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContent).isEqualTo(responseContent) + } + @Test fun ofInputImage() { - val inputImage = ResponseInputImage.builder().detail(ResponseInputImage.Detail.HIGH).build() + val inputImage = + ResponseInputImage.builder() + .detail(ResponseInputImage.Detail.HIGH) + .fileId("file_id") + .imageUrl("image_url") + .build() val responseContent = ResponseContent.ofInputImage(inputImage) @@ -33,9 +60,35 @@ internal class ResponseContentTest { assertThat(responseContent.outputRefusal()).isEmpty } + @Test + fun ofInputImageRoundtrip() { + val jsonMapper = jsonMapper() + val responseContent = + ResponseContent.ofInputImage( + ResponseInputImage.builder() + .detail(ResponseInputImage.Detail.HIGH) + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + + val roundtrippedResponseContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContent).isEqualTo(responseContent) + } + @Test fun ofInputFile() { - val inputFile = ResponseInputFile.builder().build() + val inputFile = + ResponseInputFile.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() val responseContent = ResponseContent.ofInputFile(inputFile) @@ -46,6 +99,27 @@ internal class ResponseContentTest { assertThat(responseContent.outputRefusal()).isEmpty } + @Test + fun ofInputFileRoundtrip() { + val jsonMapper = jsonMapper() + val responseContent = + ResponseContent.ofInputFile( + ResponseInputFile.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() + ) + + val roundtrippedResponseContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContent).isEqualTo(responseContent) + } + @Test fun ofOutputText() { val outputText = @@ -68,6 +142,31 @@ internal class ResponseContentTest { assertThat(responseContent.outputRefusal()).isEmpty } + @Test + fun ofOutputTextRoundtrip() { + val jsonMapper = jsonMapper() + val responseContent = + ResponseContent.ofOutputText( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + + val roundtrippedResponseContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContent).isEqualTo(responseContent) + } + @Test fun ofOutputRefusal() { val outputRefusal = ResponseOutputRefusal.builder().refusal("refusal").build() @@ -80,4 +179,39 @@ internal class ResponseContentTest { assertThat(responseContent.outputText()).isEmpty assertThat(responseContent.outputRefusal()).contains(outputRefusal) } + + @Test + fun ofOutputRefusalRoundtrip() { + val jsonMapper = jsonMapper() + val responseContent = + ResponseContent.ofOutputRefusal( + ResponseOutputRefusal.builder().refusal("refusal").build() + ) + + val roundtrippedResponseContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseContent).isEqualTo(responseContent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseContent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseContent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCreatedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCreatedEventTest.kt index a3975222e..6351b9e36 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCreatedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCreatedEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.ChatModel import com.openai.models.ComparisonFilter import com.openai.models.Reasoning @@ -213,4 +215,117 @@ internal class ResponseCreatedEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseCreatedEvent = + ResponseCreatedEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + + val roundtrippedResponseCreatedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseCreatedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseCreatedEvent).isEqualTo(responseCreatedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorEventTest.kt index d15a5a9c1..8e0738f60 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,19 @@ internal class ResponseErrorEventTest { assertThat(responseErrorEvent.message()).isEqualTo("message") assertThat(responseErrorEvent.param()).contains("param") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseErrorEvent = + ResponseErrorEvent.builder().code("code").message("message").param("param").build() + + val roundtrippedResponseErrorEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseErrorEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseErrorEvent).isEqualTo(responseErrorEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorTest.kt index 6eefcd16d..61131a59d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseErrorTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,19 @@ internal class ResponseErrorTest { assertThat(responseError.code()).isEqualTo(ResponseError.Code.SERVER_ERROR) assertThat(responseError.message()).isEqualTo("message") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseError = + ResponseError.builder().code(ResponseError.Code.SERVER_ERROR).message("message").build() + + val roundtrippedResponseError = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseError), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseError).isEqualTo(responseError) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFailedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFailedEventTest.kt index e049c37eb..366cfa9f2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFailedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFailedEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.ChatModel import com.openai.models.ComparisonFilter import com.openai.models.Reasoning @@ -213,4 +215,117 @@ internal class ResponseFailedEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFailedEvent = + ResponseFailedEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + + val roundtrippedResponseFailedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFailedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFailedEvent).isEqualTo(responseFailedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEventTest.kt index 13d40ba35..63d49acb7 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallCompletedEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ResponseFileSearchCallCompletedEventTest { assertThat(responseFileSearchCallCompletedEvent.itemId()).isEqualTo("item_id") assertThat(responseFileSearchCallCompletedEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFileSearchCallCompletedEvent = + ResponseFileSearchCallCompletedEvent.builder().itemId("item_id").outputIndex(0L).build() + + val roundtrippedResponseFileSearchCallCompletedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFileSearchCallCompletedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFileSearchCallCompletedEvent) + .isEqualTo(responseFileSearchCallCompletedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEventTest.kt index 9b4748238..1111b05c5 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallInProgressEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,4 +20,23 @@ internal class ResponseFileSearchCallInProgressEventTest { assertThat(responseFileSearchCallInProgressEvent.itemId()).isEqualTo("item_id") assertThat(responseFileSearchCallInProgressEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFileSearchCallInProgressEvent = + ResponseFileSearchCallInProgressEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + + val roundtrippedResponseFileSearchCallInProgressEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFileSearchCallInProgressEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFileSearchCallInProgressEvent) + .isEqualTo(responseFileSearchCallInProgressEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEventTest.kt index 80d605945..797fb1ab1 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchCallSearchingEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ResponseFileSearchCallSearchingEventTest { assertThat(responseFileSearchCallSearchingEvent.itemId()).isEqualTo("item_id") assertThat(responseFileSearchCallSearchingEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFileSearchCallSearchingEvent = + ResponseFileSearchCallSearchingEvent.builder().itemId("item_id").outputIndex(0L).build() + + val roundtrippedResponseFileSearchCallSearchingEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFileSearchCallSearchingEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFileSearchCallSearchingEvent) + .isEqualTo(responseFileSearchCallSearchingEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt index 4d9a4c3c5..50752fd39 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -50,4 +52,36 @@ internal class ResponseFileSearchToolCallTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFileSearchToolCall = + ResponseFileSearchToolCall.builder() + .id("id") + .addQuery("string") + .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) + .build() + + val roundtrippedResponseFileSearchToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFileSearchToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFileSearchToolCall).isEqualTo(responseFileSearchToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextConfigTest.kt index 28fdbc8b3..e427c6381 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextConfigTest.kt @@ -2,11 +2,17 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.ResponseFormatJsonObject import com.openai.models.ResponseFormatText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseFormatTextConfigTest { @@ -21,6 +27,21 @@ internal class ResponseFormatTextConfigTest { assertThat(responseFormatTextConfig.jsonObject()).isEmpty } + @Test + fun ofTextRoundtrip() { + val jsonMapper = jsonMapper() + val responseFormatTextConfig = + ResponseFormatTextConfig.ofText(ResponseFormatText.builder().build()) + + val roundtrippedResponseFormatTextConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatTextConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatTextConfig).isEqualTo(responseFormatTextConfig) + } + @Test fun ofJsonSchema() { val jsonSchema = @@ -31,6 +52,8 @@ internal class ResponseFormatTextConfigTest { .putAdditionalProperty("foo", JsonValue.from("bar")) .build() ) + .description("description") + .strict(true) .build() val responseFormatTextConfig = ResponseFormatTextConfig.ofJsonSchema(jsonSchema) @@ -40,6 +63,32 @@ internal class ResponseFormatTextConfigTest { assertThat(responseFormatTextConfig.jsonObject()).isEmpty } + @Test + fun ofJsonSchemaRoundtrip() { + val jsonMapper = jsonMapper() + val responseFormatTextConfig = + ResponseFormatTextConfig.ofJsonSchema( + ResponseFormatTextJsonSchemaConfig.builder() + .name("name") + .schema( + ResponseFormatTextJsonSchemaConfig.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .description("description") + .strict(true) + .build() + ) + + val roundtrippedResponseFormatTextConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatTextConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatTextConfig).isEqualTo(responseFormatTextConfig) + } + @Test fun ofJsonObject() { val jsonObject = ResponseFormatJsonObject.builder().build() @@ -50,4 +99,37 @@ internal class ResponseFormatTextConfigTest { assertThat(responseFormatTextConfig.jsonSchema()).isEmpty assertThat(responseFormatTextConfig.jsonObject()).contains(jsonObject) } + + @Test + fun ofJsonObjectRoundtrip() { + val jsonMapper = jsonMapper() + val responseFormatTextConfig = + ResponseFormatTextConfig.ofJsonObject(ResponseFormatJsonObject.builder().build()) + + val roundtrippedResponseFormatTextConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatTextConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatTextConfig).isEqualTo(responseFormatTextConfig) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseFormatTextConfig = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseFormatTextConfig.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfigTest.kt index 9c60a875e..a0841defd 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFormatTextJsonSchemaConfigTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -32,4 +34,29 @@ internal class ResponseFormatTextJsonSchemaConfigTest { assertThat(responseFormatTextJsonSchemaConfig.description()).contains("description") assertThat(responseFormatTextJsonSchemaConfig.strict()).contains(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFormatTextJsonSchemaConfig = + ResponseFormatTextJsonSchemaConfig.builder() + .name("name") + .schema( + ResponseFormatTextJsonSchemaConfig.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .description("description") + .strict(true) + .build() + + val roundtrippedResponseFormatTextJsonSchemaConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFormatTextJsonSchemaConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFormatTextJsonSchemaConfig) + .isEqualTo(responseFormatTextJsonSchemaConfig) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEventTest.kt index 6147cff4c..fa59d3747 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -20,4 +22,24 @@ internal class ResponseFunctionCallArgumentsDeltaEventTest { assertThat(responseFunctionCallArgumentsDeltaEvent.itemId()).isEqualTo("item_id") assertThat(responseFunctionCallArgumentsDeltaEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionCallArgumentsDeltaEvent = + ResponseFunctionCallArgumentsDeltaEvent.builder() + .delta("delta") + .itemId("item_id") + .outputIndex(0L) + .build() + + val roundtrippedResponseFunctionCallArgumentsDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionCallArgumentsDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionCallArgumentsDeltaEvent) + .isEqualTo(responseFunctionCallArgumentsDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt index e6e16f608..719138dc9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -20,4 +22,24 @@ internal class ResponseFunctionCallArgumentsDoneEventTest { assertThat(responseFunctionCallArgumentsDoneEvent.itemId()).isEqualTo("item_id") assertThat(responseFunctionCallArgumentsDoneEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionCallArgumentsDoneEvent = + ResponseFunctionCallArgumentsDoneEvent.builder() + .arguments("arguments") + .itemId("item_id") + .outputIndex(0L) + .build() + + val roundtrippedResponseFunctionCallArgumentsDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionCallArgumentsDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionCallArgumentsDoneEvent) + .isEqualTo(responseFunctionCallArgumentsDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallItemTest.kt index 8fd8f54c9..c56a8efad 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallItemTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -25,4 +27,25 @@ internal class ResponseFunctionToolCallItemTest { assertThat(responseFunctionToolCallItem.status()) .contains(ResponseFunctionToolCall.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionToolCallItem = + ResponseFunctionToolCallItem.builder() + .arguments("arguments") + .callId("call_id") + .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseFunctionToolCallItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionToolCallItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionToolCallItem).isEqualTo(responseFunctionToolCallItem) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt index 3eda88db5..090296328 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -23,4 +25,25 @@ internal class ResponseFunctionToolCallOutputItemTest { assertThat(responseFunctionToolCallOutputItem.status()) .contains(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionToolCallOutputItem = + ResponseFunctionToolCallOutputItem.builder() + .id("id") + .callId("call_id") + .output("output") + .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseFunctionToolCallOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionToolCallOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionToolCallOutputItem) + .isEqualTo(responseFunctionToolCallOutputItem) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallTest.kt index d9d65a19c..92e7d2234 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -25,4 +27,25 @@ internal class ResponseFunctionToolCallTest { assertThat(responseFunctionToolCall.status()) .contains(ResponseFunctionToolCall.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionToolCall = + ResponseFunctionToolCall.builder() + .arguments("arguments") + .callId("call_id") + .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseFunctionToolCall = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionToolCall), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionToolCall).isEqualTo(responseFunctionToolCall) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt index 5a03c8c98..f93dd7d8c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionWebSearchTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -19,4 +21,22 @@ internal class ResponseFunctionWebSearchTest { assertThat(responseFunctionWebSearch.status()) .isEqualTo(ResponseFunctionWebSearch.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionWebSearch = + ResponseFunctionWebSearch.builder() + .id("id") + .status(ResponseFunctionWebSearch.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseFunctionWebSearch = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionWebSearch), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionWebSearch).isEqualTo(responseFunctionWebSearch) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInProgressEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInProgressEventTest.kt index 40ec894d7..1d98f0f57 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInProgressEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInProgressEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.ChatModel import com.openai.models.ComparisonFilter import com.openai.models.Reasoning @@ -213,4 +215,117 @@ internal class ResponseInProgressEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInProgressEvent = + ResponseInProgressEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + + val roundtrippedResponseInProgressEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInProgressEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInProgressEvent).isEqualTo(responseInProgressEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseIncompleteEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseIncompleteEventTest.kt index 22223407f..35f5efc71 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseIncompleteEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseIncompleteEventTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.ChatModel import com.openai.models.ComparisonFilter import com.openai.models.Reasoning @@ -213,4 +215,117 @@ internal class ResponseIncompleteEventTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseIncompleteEvent = + ResponseIncompleteEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + + val roundtrippedResponseIncompleteEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseIncompleteEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseIncompleteEvent).isEqualTo(responseIncompleteEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputAudioTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputAudioTest.kt index c08fa15a8..8a96a6c6d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputAudioTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputAudioTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,19 @@ internal class ResponseInputAudioTest { assertThat(responseInputAudio.data()).isEqualTo("data") assertThat(responseInputAudio.format()).isEqualTo(ResponseInputAudio.Format.MP3) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputAudio = + ResponseInputAudio.builder().data("data").format(ResponseInputAudio.Format.MP3).build() + + val roundtrippedResponseInputAudio = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputAudio), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputAudio).isEqualTo(responseInputAudio) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt index 9bd4c8e39..b28c4c28e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputContentTest.kt @@ -2,8 +2,15 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseInputContentTest { @@ -18,9 +25,29 @@ internal class ResponseInputContentTest { assertThat(responseInputContent.inputFile()).isEmpty } + @Test + fun ofInputTextRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputContent = + ResponseInputContent.ofInputText(ResponseInputText.builder().text("text").build()) + + val roundtrippedResponseInputContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputContent).isEqualTo(responseInputContent) + } + @Test fun ofInputImage() { - val inputImage = ResponseInputImage.builder().detail(ResponseInputImage.Detail.HIGH).build() + val inputImage = + ResponseInputImage.builder() + .detail(ResponseInputImage.Detail.HIGH) + .fileId("file_id") + .imageUrl("image_url") + .build() val responseInputContent = ResponseInputContent.ofInputImage(inputImage) @@ -29,9 +56,35 @@ internal class ResponseInputContentTest { assertThat(responseInputContent.inputFile()).isEmpty } + @Test + fun ofInputImageRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputContent = + ResponseInputContent.ofInputImage( + ResponseInputImage.builder() + .detail(ResponseInputImage.Detail.HIGH) + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + + val roundtrippedResponseInputContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputContent).isEqualTo(responseInputContent) + } + @Test fun ofInputFile() { - val inputFile = ResponseInputFile.builder().build() + val inputFile = + ResponseInputFile.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() val responseInputContent = ResponseInputContent.ofInputFile(inputFile) @@ -39,4 +92,43 @@ internal class ResponseInputContentTest { assertThat(responseInputContent.inputImage()).isEmpty assertThat(responseInputContent.inputFile()).contains(inputFile) } + + @Test + fun ofInputFileRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputContent = + ResponseInputContent.ofInputFile( + ResponseInputFile.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() + ) + + val roundtrippedResponseInputContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputContent).isEqualTo(responseInputContent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseInputContent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseInputContent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileTest.kt index 01107d8c2..059315369 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -20,4 +22,23 @@ internal class ResponseInputFileTest { assertThat(responseInputFile.fileId()).contains("file_id") assertThat(responseInputFile.filename()).contains("filename") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputFile = + ResponseInputFile.builder() + .fileData("file_data") + .fileId("file_id") + .filename("filename") + .build() + + val roundtrippedResponseInputFile = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputFile), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputFile).isEqualTo(responseInputFile) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt index 4cf5d7de6..b6ae8c26a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -20,4 +22,23 @@ internal class ResponseInputImageTest { assertThat(responseInputImage.fileId()).contains("file_id") assertThat(responseInputImage.imageUrl()).contains("image_url") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputImage = + ResponseInputImage.builder() + .detail(ResponseInputImage.Detail.HIGH) + .fileId("file_id") + .imageUrl("image_url") + .build() + + val roundtrippedResponseInputImage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputImage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputImage).isEqualTo(responseInputImage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt index 657904a4b..820cb2155 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt @@ -2,15 +2,26 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseInputItemTest { @Test fun ofEasyInputMessage() { val easyInputMessage = - EasyInputMessage.builder().content("string").role(EasyInputMessage.Role.USER).build() + EasyInputMessage.builder() + .content("string") + .role(EasyInputMessage.Role.USER) + .type(EasyInputMessage.Type.MESSAGE) + .build() val responseInputItem = ResponseInputItem.ofEasyInputMessage(easyInputMessage) @@ -27,12 +38,35 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofEasyInputMessageRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofEasyInputMessage( + EasyInputMessage.builder() + .content("string") + .role(EasyInputMessage.Role.USER) + .type(EasyInputMessage.Type.MESSAGE) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofMessage() { val message = ResponseInputItem.Message.builder() .addInputTextContent("text") .role(ResponseInputItem.Message.Role.USER) + .status(ResponseInputItem.Message.Status.IN_PROGRESS) + .type(ResponseInputItem.Message.Type.MESSAGE) .build() val responseInputItem = ResponseInputItem.ofMessage(message) @@ -50,6 +84,28 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofMessageRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofMessage( + ResponseInputItem.Message.builder() + .addInputTextContent("text") + .role(ResponseInputItem.Message.Role.USER) + .status(ResponseInputItem.Message.Status.IN_PROGRESS) + .type(ResponseInputItem.Message.Type.MESSAGE) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofResponseOutputMessage() { val responseOutputMessage = @@ -84,6 +140,37 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofResponseOutputMessageRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofResponseOutputMessage( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofFileSearchCall() { val fileSearchCall = @@ -91,6 +178,19 @@ internal class ResponseInputItemTest { .id("id") .addQuery("string") .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) .build() val responseInputItem = ResponseInputItem.ofFileSearchCall(fileSearchCall) @@ -108,6 +208,40 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofFileSearchCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofFileSearchCall( + ResponseFileSearchToolCall.builder() + .id("id") + .addQuery("string") + .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofComputerCall() { val computerCall = @@ -147,12 +281,62 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofComputerCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofComputerCall( + ResponseComputerToolCall.builder() + .id("id") + .action( + ResponseComputerToolCall.Action.Click.builder() + .button(ResponseComputerToolCall.Action.Click.Button.LEFT) + .x(0L) + .y(0L) + .build() + ) + .callId("call_id") + .addPendingSafetyCheck( + ResponseComputerToolCall.PendingSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCall.Status.IN_PROGRESS) + .type(ResponseComputerToolCall.Type.COMPUTER_CALL) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofComputerCallOutput() { val computerCallOutput = ResponseInputItem.ComputerCallOutput.builder() .callId("call_id") - .output(ResponseComputerToolCallOutputScreenshot.builder().build()) + .output( + ResponseComputerToolCallOutputScreenshot.builder() + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + .id("id") + .addAcknowledgedSafetyCheck( + ResponseInputItem.ComputerCallOutput.AcknowledgedSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseInputItem.ComputerCallOutput.Status.IN_PROGRESS) .build() val responseInputItem = ResponseInputItem.ofComputerCallOutput(computerCallOutput) @@ -170,6 +354,40 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofComputerCallOutputRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofComputerCallOutput( + ResponseInputItem.ComputerCallOutput.builder() + .callId("call_id") + .output( + ResponseComputerToolCallOutputScreenshot.builder() + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + .id("id") + .addAcknowledgedSafetyCheck( + ResponseInputItem.ComputerCallOutput.AcknowledgedSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseInputItem.ComputerCallOutput.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofWebSearchCall() { val webSearchCall = @@ -193,6 +411,26 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofWebSearchCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofWebSearchCall( + ResponseFunctionWebSearch.builder() + .id("id") + .status(ResponseFunctionWebSearch.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofFunctionCall() { val functionCall = @@ -200,6 +438,8 @@ internal class ResponseInputItemTest { .arguments("arguments") .callId("call_id") .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) .build() val responseInputItem = ResponseInputItem.ofFunctionCall(functionCall) @@ -217,12 +457,37 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofFunctionCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofFunctionCall( + ResponseFunctionToolCall.builder() + .arguments("arguments") + .callId("call_id") + .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofFunctionCallOutput() { val functionCallOutput = ResponseInputItem.FunctionCallOutput.builder() .callId("call_id") .output("output") + .id("id") + .status(ResponseInputItem.FunctionCallOutput.Status.IN_PROGRESS) .build() val responseInputItem = ResponseInputItem.ofFunctionCallOutput(functionCallOutput) @@ -240,12 +505,35 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofFunctionCallOutputRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofFunctionCallOutput( + ResponseInputItem.FunctionCallOutput.builder() + .callId("call_id") + .output("output") + .id("id") + .status(ResponseInputItem.FunctionCallOutput.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofReasoning() { val reasoning = ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() val responseInputItem = ResponseInputItem.ofReasoning(reasoning) @@ -263,6 +551,27 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.itemReference()).isEmpty } + @Test + fun ofReasoningRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofReasoning( + ResponseReasoningItem.builder() + .id("id") + .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .status(ResponseReasoningItem.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + @Test fun ofItemReference() { val itemReference = ResponseInputItem.ItemReference.builder().id("id").build() @@ -281,4 +590,39 @@ internal class ResponseInputItemTest { assertThat(responseInputItem.reasoning()).isEmpty assertThat(responseInputItem.itemReference()).contains(itemReference) } + + @Test + fun ofItemReferenceRoundtrip() { + val jsonMapper = jsonMapper() + val responseInputItem = + ResponseInputItem.ofItemReference( + ResponseInputItem.ItemReference.builder().id("id").build() + ) + + val roundtrippedResponseInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputItem).isEqualTo(responseInputItem) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseInputItem = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseInputItem.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputMessageItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputMessageItemTest.kt index 0994fe5c8..1ddefa101 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputMessageItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputMessageItemTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,4 +30,25 @@ internal class ResponseInputMessageItemTest { .contains(ResponseInputMessageItem.Status.IN_PROGRESS) assertThat(responseInputMessageItem.type()).contains(ResponseInputMessageItem.Type.MESSAGE) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputMessageItem = + ResponseInputMessageItem.builder() + .id("id") + .addInputTextContent("text") + .role(ResponseInputMessageItem.Role.USER) + .status(ResponseInputMessageItem.Status.IN_PROGRESS) + .type(ResponseInputMessageItem.Type.MESSAGE) + .build() + + val roundtrippedResponseInputMessageItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputMessageItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputMessageItem).isEqualTo(responseInputMessageItem) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextTest.kt index 01513e63e..96c81e8a0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class ResponseInputTextTest { assertThat(responseInputText.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputText = ResponseInputText.builder().text("text").build() + + val roundtrippedResponseInputText = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputText), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputText).isEqualTo(responseInputText) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt index ecba5bc8d..2e4bfed33 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt @@ -2,8 +2,15 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseItemTest { @@ -14,6 +21,8 @@ internal class ResponseItemTest { .id("id") .addInputTextContent("text") .role(ResponseInputMessageItem.Role.USER) + .status(ResponseInputMessageItem.Status.IN_PROGRESS) + .type(ResponseInputMessageItem.Type.MESSAGE) .build() val responseItem = ResponseItem.ofResponseInputMessageItem(responseInputMessageItem) @@ -28,6 +37,29 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofResponseInputMessageItemRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofResponseInputMessageItem( + ResponseInputMessageItem.builder() + .id("id") + .addInputTextContent("text") + .role(ResponseInputMessageItem.Role.USER) + .status(ResponseInputMessageItem.Status.IN_PROGRESS) + .type(ResponseInputMessageItem.Type.MESSAGE) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofResponseOutputMessage() { val responseOutputMessage = @@ -59,6 +91,37 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofResponseOutputMessageRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofResponseOutputMessage( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofFileSearchCall() { val fileSearchCall = @@ -66,6 +129,19 @@ internal class ResponseItemTest { .id("id") .addQuery("string") .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) .build() val responseItem = ResponseItem.ofFileSearchCall(fileSearchCall) @@ -80,6 +156,40 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofFileSearchCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofFileSearchCall( + ResponseFileSearchToolCall.builder() + .id("id") + .addQuery("string") + .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofComputerCall() { val computerCall = @@ -116,13 +226,62 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofComputerCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofComputerCall( + ResponseComputerToolCall.builder() + .id("id") + .action( + ResponseComputerToolCall.Action.Click.builder() + .button(ResponseComputerToolCall.Action.Click.Button.LEFT) + .x(0L) + .y(0L) + .build() + ) + .callId("call_id") + .addPendingSafetyCheck( + ResponseComputerToolCall.PendingSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCall.Status.IN_PROGRESS) + .type(ResponseComputerToolCall.Type.COMPUTER_CALL) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofComputerCallOutput() { val computerCallOutput = ResponseComputerToolCallOutputItem.builder() .id("id") .callId("call_id") - .output(ResponseComputerToolCallOutputScreenshot.builder().build()) + .output( + ResponseComputerToolCallOutputScreenshot.builder() + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + .addAcknowledgedSafetyCheck( + ResponseComputerToolCallOutputItem.AcknowledgedSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCallOutputItem.Status.IN_PROGRESS) .build() val responseItem = ResponseItem.ofComputerCallOutput(computerCallOutput) @@ -137,6 +296,40 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofComputerCallOutputRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofComputerCallOutput( + ResponseComputerToolCallOutputItem.builder() + .id("id") + .callId("call_id") + .output( + ResponseComputerToolCallOutputScreenshot.builder() + .fileId("file_id") + .imageUrl("image_url") + .build() + ) + .addAcknowledgedSafetyCheck( + ResponseComputerToolCallOutputItem.AcknowledgedSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCallOutputItem.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofWebSearchCall() { val webSearchCall = @@ -157,6 +350,26 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofWebSearchCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofWebSearchCall( + ResponseFunctionWebSearch.builder() + .id("id") + .status(ResponseFunctionWebSearch.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofFunctionCall() { val functionCall = @@ -165,6 +378,7 @@ internal class ResponseItemTest { .callId("call_id") .name("name") .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) .build() val responseItem = ResponseItem.ofFunctionCall(functionCall) @@ -179,6 +393,29 @@ internal class ResponseItemTest { assertThat(responseItem.functionCallOutput()).isEmpty } + @Test + fun ofFunctionCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofFunctionCall( + ResponseFunctionToolCallItem.builder() + .arguments("arguments") + .callId("call_id") + .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + @Test fun ofFunctionCallOutput() { val functionCallOutput = @@ -186,6 +423,7 @@ internal class ResponseItemTest { .id("id") .callId("call_id") .output("output") + .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() val responseItem = ResponseItem.ofFunctionCallOutput(functionCallOutput) @@ -199,4 +437,43 @@ internal class ResponseItemTest { assertThat(responseItem.functionCall()).isEmpty assertThat(responseItem.functionCallOutput()).contains(functionCallOutput) } + + @Test + fun ofFunctionCallOutputRoundtrip() { + val jsonMapper = jsonMapper() + val responseItem = + ResponseItem.ofFunctionCallOutput( + ResponseFunctionToolCallOutputItem.builder() + .id("id") + .callId("call_id") + .output("output") + .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItem).isEqualTo(responseItem) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseItem = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseItem.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputAudioTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputAudioTest.kt index 63695a45b..312692f99 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputAudioTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputAudioTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,19 @@ internal class ResponseOutputAudioTest { assertThat(responseOutputAudio.data()).isEqualTo("data") assertThat(responseOutputAudio.transcript()).isEqualTo("transcript") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseOutputAudio = + ResponseOutputAudio.builder().data("data").transcript("transcript").build() + + val roundtrippedResponseOutputAudio = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputAudio), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputAudio).isEqualTo(responseOutputAudio) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemAddedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemAddedEventTest.kt index 55829a76a..ec2981af6 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemAddedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemAddedEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -53,4 +55,38 @@ internal class ResponseOutputItemAddedEventTest { ) assertThat(responseOutputItemAddedEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItemAddedEvent = + ResponseOutputItemAddedEvent.builder() + .item( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + + val roundtrippedResponseOutputItemAddedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItemAddedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItemAddedEvent).isEqualTo(responseOutputItemAddedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemDoneEventTest.kt index 091cc966f..d555f9404 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemDoneEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -53,4 +55,38 @@ internal class ResponseOutputItemDoneEventTest { ) assertThat(responseOutputItemDoneEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItemDoneEvent = + ResponseOutputItemDoneEvent.builder() + .item( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + + val roundtrippedResponseOutputItemDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItemDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItemDoneEvent).isEqualTo(responseOutputItemDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt index 8356aa94a..253942266 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt @@ -2,8 +2,15 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseOutputItemTest { @@ -36,6 +43,37 @@ internal class ResponseOutputItemTest { assertThat(responseOutputItem.reasoning()).isEmpty } + @Test + fun ofMessageRoundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItem = + ResponseOutputItem.ofMessage( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItem).isEqualTo(responseOutputItem) + } + @Test fun ofFileSearchCall() { val fileSearchCall = @@ -43,6 +81,19 @@ internal class ResponseOutputItemTest { .id("id") .addQuery("string") .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) .build() val responseOutputItem = ResponseOutputItem.ofFileSearchCall(fileSearchCall) @@ -55,6 +106,40 @@ internal class ResponseOutputItemTest { assertThat(responseOutputItem.reasoning()).isEmpty } + @Test + fun ofFileSearchCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItem = + ResponseOutputItem.ofFileSearchCall( + ResponseFileSearchToolCall.builder() + .id("id") + .addQuery("string") + .status(ResponseFileSearchToolCall.Status.IN_PROGRESS) + .addResult( + ResponseFileSearchToolCall.Result.builder() + .attributes( + ResponseFileSearchToolCall.Result.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .text("text") + .build() + ) + .build() + ) + + val roundtrippedResponseOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItem).isEqualTo(responseOutputItem) + } + @Test fun ofFunctionCall() { val functionCall = @@ -62,6 +147,8 @@ internal class ResponseOutputItemTest { .arguments("arguments") .callId("call_id") .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) .build() val responseOutputItem = ResponseOutputItem.ofFunctionCall(functionCall) @@ -74,6 +161,29 @@ internal class ResponseOutputItemTest { assertThat(responseOutputItem.reasoning()).isEmpty } + @Test + fun ofFunctionCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItem = + ResponseOutputItem.ofFunctionCall( + ResponseFunctionToolCall.builder() + .arguments("arguments") + .callId("call_id") + .name("name") + .id("id") + .status(ResponseFunctionToolCall.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItem).isEqualTo(responseOutputItem) + } + @Test fun ofWebSearchCall() { val webSearchCall = @@ -92,6 +202,26 @@ internal class ResponseOutputItemTest { assertThat(responseOutputItem.reasoning()).isEmpty } + @Test + fun ofWebSearchCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItem = + ResponseOutputItem.ofWebSearchCall( + ResponseFunctionWebSearch.builder() + .id("id") + .status(ResponseFunctionWebSearch.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItem).isEqualTo(responseOutputItem) + } + @Test fun ofComputerCall() { val computerCall = @@ -126,12 +256,49 @@ internal class ResponseOutputItemTest { assertThat(responseOutputItem.reasoning()).isEmpty } + @Test + fun ofComputerCallRoundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItem = + ResponseOutputItem.ofComputerCall( + ResponseComputerToolCall.builder() + .id("id") + .action( + ResponseComputerToolCall.Action.Click.builder() + .button(ResponseComputerToolCall.Action.Click.Button.LEFT) + .x(0L) + .y(0L) + .build() + ) + .callId("call_id") + .addPendingSafetyCheck( + ResponseComputerToolCall.PendingSafetyCheck.builder() + .id("id") + .code("code") + .message("message") + .build() + ) + .status(ResponseComputerToolCall.Status.IN_PROGRESS) + .type(ResponseComputerToolCall.Type.COMPUTER_CALL) + .build() + ) + + val roundtrippedResponseOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItem).isEqualTo(responseOutputItem) + } + @Test fun ofReasoning() { val reasoning = ResponseReasoningItem.builder() .id("id") .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .status(ResponseReasoningItem.Status.IN_PROGRESS) .build() val responseOutputItem = ResponseOutputItem.ofReasoning(reasoning) @@ -143,4 +310,43 @@ internal class ResponseOutputItemTest { assertThat(responseOutputItem.computerCall()).isEmpty assertThat(responseOutputItem.reasoning()).contains(reasoning) } + + @Test + fun ofReasoningRoundtrip() { + val jsonMapper = jsonMapper() + val responseOutputItem = + ResponseOutputItem.ofReasoning( + ResponseReasoningItem.builder() + .id("id") + .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .status(ResponseReasoningItem.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedResponseOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputItem).isEqualTo(responseOutputItem) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseOutputItem = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseOutputItem.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputMessageTest.kt index fe5a11aaa..5f16b5e03 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputMessageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputMessageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -44,4 +46,33 @@ internal class ResponseOutputMessageTest { assertThat(responseOutputMessage.status()) .isEqualTo(ResponseOutputMessage.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseOutputMessage = + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseOutputMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputMessage).isEqualTo(responseOutputMessage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputRefusalTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputRefusalTest.kt index cbe82bc48..70f32382d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputRefusalTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputRefusalTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class ResponseOutputRefusalTest { assertThat(responseOutputRefusal.refusal()).isEqualTo("refusal") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseOutputRefusal = ResponseOutputRefusal.builder().refusal("refusal").build() + + val roundtrippedResponseOutputRefusal = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputRefusal), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputRefusal).isEqualTo(responseOutputRefusal) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputTextTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputTextTest.kt index ad6821667..977ea331f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputTextTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputTextTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -31,4 +33,27 @@ internal class ResponseOutputTextTest { ) assertThat(responseOutputText.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseOutputText = + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + + val roundtrippedResponseOutputText = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseOutputText), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseOutputText).isEqualTo(responseOutputText) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt index 7163cf41c..1cfdfeef3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseReasoningItemTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,23 @@ internal class ResponseReasoningItemTest { assertThat(responseReasoningItem.status()) .contains(ResponseReasoningItem.Status.IN_PROGRESS) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseReasoningItem = + ResponseReasoningItem.builder() + .id("id") + .addSummary(ResponseReasoningItem.Summary.builder().text("text").build()) + .status(ResponseReasoningItem.Status.IN_PROGRESS) + .build() + + val roundtrippedResponseReasoningItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseReasoningItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseReasoningItem).isEqualTo(responseReasoningItem) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDeltaEventTest.kt index 63cd22624..bc1ecec78 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,24 @@ internal class ResponseRefusalDeltaEventTest { assertThat(responseRefusalDeltaEvent.itemId()).isEqualTo("item_id") assertThat(responseRefusalDeltaEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseRefusalDeltaEvent = + ResponseRefusalDeltaEvent.builder() + .contentIndex(0L) + .delta("delta") + .itemId("item_id") + .outputIndex(0L) + .build() + + val roundtrippedResponseRefusalDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseRefusalDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseRefusalDeltaEvent).isEqualTo(responseRefusalDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDoneEventTest.kt index 91f93efa8..e7034632e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseRefusalDoneEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,24 @@ internal class ResponseRefusalDoneEventTest { assertThat(responseRefusalDoneEvent.outputIndex()).isEqualTo(0L) assertThat(responseRefusalDoneEvent.refusal()).isEqualTo("refusal") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseRefusalDoneEvent = + ResponseRefusalDoneEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .refusal("refusal") + .build() + + val roundtrippedResponseRefusalDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseRefusalDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseRefusalDoneEvent).isEqualTo(responseRefusalDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt index 6993e4491..96ca0e351 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt @@ -2,10 +2,20 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import com.openai.models.ChatModel +import com.openai.models.ComparisonFilter +import com.openai.models.Reasoning +import com.openai.models.ReasoningEffort +import com.openai.models.ResponseFormatText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ResponseStreamEventTest { @@ -49,6 +59,23 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofAudioDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofAudioDelta( + ResponseAudioDeltaEvent.builder().delta("delta").build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofAudioDone() { val audioDone = ResponseAudioDoneEvent.builder().build() @@ -89,6 +116,21 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofAudioDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofAudioDone(ResponseAudioDoneEvent.builder().build()) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofAudioTranscriptDelta() { val audioTranscriptDelta = @@ -130,6 +172,23 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofAudioTranscriptDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofAudioTranscriptDelta( + ResponseAudioTranscriptDeltaEvent.builder().delta("delta").build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofAudioTranscriptDone() { val audioTranscriptDone = ResponseAudioTranscriptDoneEvent.builder().build() @@ -170,6 +229,23 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofAudioTranscriptDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofAudioTranscriptDone( + ResponseAudioTranscriptDoneEvent.builder().build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCodeInterpreterCallCodeDelta() { val codeInterpreterCallCodeDelta = @@ -216,6 +292,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCodeInterpreterCallCodeDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCodeInterpreterCallCodeDelta( + ResponseCodeInterpreterCallCodeDeltaEvent.builder() + .delta("delta") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCodeInterpreterCallCodeDone() { val codeInterpreterCallCodeDone = @@ -259,6 +355,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCodeInterpreterCallCodeDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCodeInterpreterCallCodeDone( + ResponseCodeInterpreterCallCodeDoneEvent.builder() + .code("code") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCodeInterpreterCallCompleted() { val codeInterpreterCallCompleted = @@ -312,6 +428,33 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCodeInterpreterCallCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCodeInterpreterCallCompleted( + ResponseCodeInterpreterCallCompletedEvent.builder() + .codeInterpreterCall( + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCodeInterpreterCallInProgress() { val codeInterpreterCallInProgress = @@ -365,6 +508,33 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCodeInterpreterCallInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCodeInterpreterCallInProgress( + ResponseCodeInterpreterCallInProgressEvent.builder() + .codeInterpreterCall( + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCodeInterpreterCallInterpreting() { val codeInterpreterCallInterpreting = @@ -418,6 +588,33 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCodeInterpreterCallInterpretingRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCodeInterpreterCallInterpreting( + ResponseCodeInterpreterCallInterpretingEvent.builder() + .codeInterpreterCall( + ResponseCodeInterpreterToolCall.builder() + .id("id") + .code("code") + .addLogsResult("logs") + .status(ResponseCodeInterpreterToolCall.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCompleted() { val completed = @@ -432,7 +629,11 @@ internal class ResponseStreamEventTest { .message("message") .build() ) - .incompleteDetails(Response.IncompleteDetails.builder().build()) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) .instructions("instructions") .metadata( Response.Metadata.builder() @@ -460,8 +661,59 @@ internal class ResponseStreamEventTest { .parallelToolCalls(true) .temperature(1.0) .toolChoice(ToolChoiceOptions.NONE) - .addFileSearchTool(listOf("string")) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") .build() ) .build() @@ -502,6 +754,121 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCompleted( + ResponseCompletedEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofContentPartAdded() { val contentPartAdded = @@ -558,6 +925,38 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofContentPartAddedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofContentPartAdded( + ResponseContentPartAddedEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .part( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofContentPartDone() { val contentPartDone = @@ -614,6 +1013,38 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofContentPartDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofContentPartDone( + ResponseContentPartDoneEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .part( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofCreated() { val created = @@ -628,7 +1059,11 @@ internal class ResponseStreamEventTest { .message("message") .build() ) - .incompleteDetails(Response.IncompleteDetails.builder().build()) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) .instructions("instructions") .metadata( Response.Metadata.builder() @@ -656,8 +1091,59 @@ internal class ResponseStreamEventTest { .parallelToolCalls(true) .temperature(1.0) .toolChoice(ToolChoiceOptions.NONE) - .addFileSearchTool(listOf("string")) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") .build() ) .build() @@ -698,6 +1184,121 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofCreatedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofCreated( + ResponseCreatedEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofError() { val error = @@ -739,6 +1340,23 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofErrorRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofError( + ResponseErrorEvent.builder().code("code").message("message").param("param").build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofFileSearchCallCompleted() { val fileSearchCallCompleted = @@ -781,6 +1399,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofFileSearchCallCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofFileSearchCallCompleted( + ResponseFileSearchCallCompletedEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofFileSearchCallInProgress() { val fileSearchCallInProgress = @@ -827,6 +1465,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofFileSearchCallInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofFileSearchCallInProgress( + ResponseFileSearchCallInProgressEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofFileSearchCallSearching() { val fileSearchCallSearching = @@ -869,6 +1527,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofFileSearchCallSearchingRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofFileSearchCallSearching( + ResponseFileSearchCallSearchingEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofFunctionCallArgumentsDelta() { val functionCallArgumentsDelta = @@ -916,6 +1594,27 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofFunctionCallArgumentsDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofFunctionCallArgumentsDelta( + ResponseFunctionCallArgumentsDeltaEvent.builder() + .delta("delta") + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofFunctionCallArgumentsDone() { val functionCallArgumentsDone = @@ -963,6 +1662,27 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofFunctionCallArgumentsDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofFunctionCallArgumentsDone( + ResponseFunctionCallArgumentsDoneEvent.builder() + .arguments("arguments") + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofInProgress() { val inProgress = @@ -977,7 +1697,11 @@ internal class ResponseStreamEventTest { .message("message") .build() ) - .incompleteDetails(Response.IncompleteDetails.builder().build()) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) .instructions("instructions") .metadata( Response.Metadata.builder() @@ -1005,8 +1729,59 @@ internal class ResponseStreamEventTest { .parallelToolCalls(true) .temperature(1.0) .toolChoice(ToolChoiceOptions.NONE) - .addFileSearchTool(listOf("string")) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") .build() ) .build() @@ -1047,6 +1822,121 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofInProgress( + ResponseInProgressEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofFailed() { val failed = @@ -1061,7 +1951,11 @@ internal class ResponseStreamEventTest { .message("message") .build() ) - .incompleteDetails(Response.IncompleteDetails.builder().build()) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) .instructions("instructions") .metadata( Response.Metadata.builder() @@ -1089,8 +1983,59 @@ internal class ResponseStreamEventTest { .parallelToolCalls(true) .temperature(1.0) .toolChoice(ToolChoiceOptions.NONE) - .addFileSearchTool(listOf("string")) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") .build() ) .build() @@ -1131,6 +2076,121 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofFailedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofFailed( + ResponseFailedEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofIncomplete() { val incomplete = @@ -1145,7 +2205,11 @@ internal class ResponseStreamEventTest { .message("message") .build() ) - .incompleteDetails(Response.IncompleteDetails.builder().build()) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) .instructions("instructions") .metadata( Response.Metadata.builder() @@ -1173,8 +2237,59 @@ internal class ResponseStreamEventTest { .parallelToolCalls(true) .temperature(1.0) .toolChoice(ToolChoiceOptions.NONE) - .addFileSearchTool(listOf("string")) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") .build() ) .build() @@ -1215,6 +2330,121 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofIncompleteRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofIncomplete( + ResponseIncompleteEvent.builder() + .response( + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder() + .cachedTokens(0L) + .build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + ) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofOutputItemAdded() { val outputItemAdded = @@ -1275,6 +2505,42 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofOutputItemAddedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofOutputItemAdded( + ResponseOutputItemAddedEvent.builder() + .item( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofOutputItemDone() { val outputItemDone = @@ -1335,6 +2601,42 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofOutputItemDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofOutputItemDone( + ResponseOutputItemDoneEvent.builder() + .item( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofRefusalDelta() { val refusalDelta = @@ -1381,6 +2683,28 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofRefusalDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofRefusalDelta( + ResponseRefusalDeltaEvent.builder() + .contentIndex(0L) + .delta("delta") + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofRefusalDone() { val refusalDone = @@ -1427,6 +2751,28 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofRefusalDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofRefusalDone( + ResponseRefusalDoneEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .refusal("refusal") + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofOutputTextAnnotationAdded() { val outputTextAnnotationAdded = @@ -1481,6 +2827,34 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofOutputTextAnnotationAddedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofOutputTextAnnotationAdded( + ResponseTextAnnotationDeltaEvent.builder() + .annotation( + ResponseTextAnnotationDeltaEvent.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .annotationIndex(0L) + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofOutputTextDelta() { val outputTextDelta = @@ -1527,6 +2901,28 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofOutputTextDeltaRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofOutputTextDelta( + ResponseTextDeltaEvent.builder() + .contentIndex(0L) + .delta("delta") + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofOutputTextDone() { val outputTextDone = @@ -1573,6 +2969,28 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofOutputTextDoneRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofOutputTextDone( + ResponseTextDoneEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .text("text") + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofWebSearchCallCompleted() { val webSearchCallCompleted = @@ -1615,6 +3033,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofWebSearchCallCompletedRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofWebSearchCallCompleted( + ResponseWebSearchCallCompletedEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofWebSearchCallInProgress() { val webSearchCallInProgress = @@ -1657,6 +3095,26 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallSearching()).isEmpty } + @Test + fun ofWebSearchCallInProgressRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofWebSearchCallInProgress( + ResponseWebSearchCallInProgressEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + @Test fun ofWebSearchCallSearching() { val webSearchCallSearching = @@ -1698,4 +3156,42 @@ internal class ResponseStreamEventTest { assertThat(responseStreamEvent.webSearchCallInProgress()).isEmpty assertThat(responseStreamEvent.webSearchCallSearching()).contains(webSearchCallSearching) } + + @Test + fun ofWebSearchCallSearchingRoundtrip() { + val jsonMapper = jsonMapper() + val responseStreamEvent = + ResponseStreamEvent.ofWebSearchCallSearching( + ResponseWebSearchCallSearchingEvent.builder() + .itemId("item_id") + .outputIndex(0L) + .build() + ) + + val roundtrippedResponseStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseStreamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseStreamEvent).isEqualTo(responseStreamEvent) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val responseStreamEvent = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { responseStreamEvent.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTest.kt index 9c53d0e94..7bbe84c00 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTest.kt @@ -2,7 +2,9 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.ChatModel import com.openai.models.ComparisonFilter import com.openai.models.Reasoning @@ -210,4 +212,109 @@ internal class ResponseTest { ) assertThat(response.user()).contains("user-1234") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val response = + Response.builder() + .id("id") + .createdAt(0.0) + .error( + ResponseError.builder() + .code(ResponseError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .incompleteDetails( + Response.IncompleteDetails.builder() + .reason(Response.IncompleteDetails.Reason.MAX_OUTPUT_TOKENS) + .build() + ) + .instructions("instructions") + .metadata( + Response.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .model(ChatModel.GPT_4O) + .addOutput( + ResponseOutputMessage.builder() + .id("id") + .addContent( + ResponseOutputText.builder() + .addAnnotation( + ResponseOutputText.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .text("text") + .build() + ) + .status(ResponseOutputMessage.Status.IN_PROGRESS) + .build() + ) + .parallelToolCalls(true) + .temperature(1.0) + .toolChoice(ToolChoiceOptions.NONE) + .addTool( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + .topP(1.0) + .maxOutputTokens(0L) + .previousResponseId("previous_response_id") + .reasoning( + Reasoning.builder() + .effort(ReasoningEffort.LOW) + .generateSummary(Reasoning.GenerateSummary.CONCISE) + .build() + ) + .status(ResponseStatus.COMPLETED) + .text( + ResponseTextConfig.builder() + .format(ResponseFormatText.builder().build()) + .build() + ) + .truncation(Response.Truncation.AUTO) + .usage( + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + ) + .user("user-1234") + .build() + + val roundtrippedResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(response), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponse).isEqualTo(response) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEventTest.kt index 822ed5605..90dd8187a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextAnnotationDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -37,4 +39,31 @@ internal class ResponseTextAnnotationDeltaEventTest { assertThat(responseTextAnnotationDeltaEvent.itemId()).isEqualTo("item_id") assertThat(responseTextAnnotationDeltaEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseTextAnnotationDeltaEvent = + ResponseTextAnnotationDeltaEvent.builder() + .annotation( + ResponseTextAnnotationDeltaEvent.Annotation.FileCitation.builder() + .fileId("file_id") + .index(0L) + .build() + ) + .annotationIndex(0L) + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .build() + + val roundtrippedResponseTextAnnotationDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseTextAnnotationDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseTextAnnotationDeltaEvent) + .isEqualTo(responseTextAnnotationDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextConfigTest.kt index 08428c225..b0fb37f37 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextConfigTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.ResponseFormatText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,4 +18,19 @@ internal class ResponseTextConfigTest { assertThat(responseTextConfig.format()) .contains(ResponseFormatTextConfig.ofText(ResponseFormatText.builder().build())) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseTextConfig = + ResponseTextConfig.builder().format(ResponseFormatText.builder().build()).build() + + val roundtrippedResponseTextConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseTextConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseTextConfig).isEqualTo(responseTextConfig) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDeltaEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDeltaEventTest.kt index a88a5a25b..31e57f167 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDeltaEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDeltaEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,24 @@ internal class ResponseTextDeltaEventTest { assertThat(responseTextDeltaEvent.itemId()).isEqualTo("item_id") assertThat(responseTextDeltaEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseTextDeltaEvent = + ResponseTextDeltaEvent.builder() + .contentIndex(0L) + .delta("delta") + .itemId("item_id") + .outputIndex(0L) + .build() + + val roundtrippedResponseTextDeltaEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseTextDeltaEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseTextDeltaEvent).isEqualTo(responseTextDeltaEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDoneEventTest.kt index a11ec330f..27d23072a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseTextDoneEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -22,4 +24,24 @@ internal class ResponseTextDoneEventTest { assertThat(responseTextDoneEvent.outputIndex()).isEqualTo(0L) assertThat(responseTextDoneEvent.text()).isEqualTo("text") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseTextDoneEvent = + ResponseTextDoneEvent.builder() + .contentIndex(0L) + .itemId("item_id") + .outputIndex(0L) + .text("text") + .build() + + val roundtrippedResponseTextDoneEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseTextDoneEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseTextDoneEvent).isEqualTo(responseTextDoneEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseUsageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseUsageTest.kt index 069ced9af..a2016e051 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseUsageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseUsageTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -30,4 +32,29 @@ internal class ResponseUsageTest { .isEqualTo(ResponseUsage.OutputTokensDetails.builder().reasoningTokens(0L).build()) assertThat(responseUsage.totalTokens()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseUsage = + ResponseUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + ResponseUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + ResponseUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + + val roundtrippedResponseUsage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseUsage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseUsage).isEqualTo(responseUsage) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEventTest.kt index 3e4b67d5e..7187771f8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallCompletedEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ResponseWebSearchCallCompletedEventTest { assertThat(responseWebSearchCallCompletedEvent.itemId()).isEqualTo("item_id") assertThat(responseWebSearchCallCompletedEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseWebSearchCallCompletedEvent = + ResponseWebSearchCallCompletedEvent.builder().itemId("item_id").outputIndex(0L).build() + + val roundtrippedResponseWebSearchCallCompletedEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseWebSearchCallCompletedEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseWebSearchCallCompletedEvent) + .isEqualTo(responseWebSearchCallCompletedEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEventTest.kt index 49d430563..72d144afb 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallInProgressEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ResponseWebSearchCallInProgressEventTest { assertThat(responseWebSearchCallInProgressEvent.itemId()).isEqualTo("item_id") assertThat(responseWebSearchCallInProgressEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseWebSearchCallInProgressEvent = + ResponseWebSearchCallInProgressEvent.builder().itemId("item_id").outputIndex(0L).build() + + val roundtrippedResponseWebSearchCallInProgressEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseWebSearchCallInProgressEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseWebSearchCallInProgressEvent) + .isEqualTo(responseWebSearchCallInProgressEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEventTest.kt index 981340c80..42b061bbe 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseWebSearchCallSearchingEventTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,20 @@ internal class ResponseWebSearchCallSearchingEventTest { assertThat(responseWebSearchCallSearchingEvent.itemId()).isEqualTo("item_id") assertThat(responseWebSearchCallSearchingEvent.outputIndex()).isEqualTo(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseWebSearchCallSearchingEvent = + ResponseWebSearchCallSearchingEvent.builder().itemId("item_id").outputIndex(0L).build() + + val roundtrippedResponseWebSearchCallSearchingEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseWebSearchCallSearchingEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseWebSearchCallSearchingEvent) + .isEqualTo(responseWebSearchCallSearchingEvent) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceFunctionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceFunctionTest.kt index c787685d8..b167fbd99 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceFunctionTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceFunctionTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,4 +15,18 @@ internal class ToolChoiceFunctionTest { assertThat(toolChoiceFunction.name()).isEqualTo("name") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val toolChoiceFunction = ToolChoiceFunction.builder().name("name").build() + + val roundtrippedToolChoiceFunction = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolChoiceFunction), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolChoiceFunction).isEqualTo(toolChoiceFunction) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceTypesTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceTypesTest.kt index 7fdfe35a8..9b842e3e0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceTypesTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolChoiceTypesTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,19 @@ internal class ToolChoiceTypesTest { assertThat(toolChoiceTypes.type()).isEqualTo(ToolChoiceTypes.Type.FILE_SEARCH) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val toolChoiceTypes = + ToolChoiceTypes.builder().type(ToolChoiceTypes.Type.FILE_SEARCH).build() + + val roundtrippedToolChoiceTypes = + jsonMapper.readValue( + jsonMapper.writeValueAsString(toolChoiceTypes), + jacksonTypeRef(), + ) + + assertThat(roundtrippedToolChoiceTypes).isEqualTo(toolChoiceTypes) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt index 63483401e..694bdf156 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ToolTest.kt @@ -2,15 +2,39 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.ComparisonFilter import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ToolTest { @Test fun ofFileSearch() { - val fileSearch = FileSearchTool.builder().addVectorStoreId("string").build() + val fileSearch = + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() val tool = Tool.ofFileSearch(fileSearch) @@ -20,6 +44,36 @@ internal class ToolTest { assertThat(tool.webSearch()).isEmpty } + @Test + fun ofFileSearchRoundtrip() { + val jsonMapper = jsonMapper() + val tool = + Tool.ofFileSearch( + FileSearchTool.builder() + .addVectorStoreId("string") + .filters( + ComparisonFilter.builder() + .key("key") + .type(ComparisonFilter.Type.EQ) + .value("string") + .build() + ) + .maxNumResults(0L) + .rankingOptions( + FileSearchTool.RankingOptions.builder() + .ranker(FileSearchTool.RankingOptions.Ranker.AUTO) + .scoreThreshold(0.0) + .build() + ) + .build() + ) + + val roundtrippedTool = + jsonMapper.readValue(jsonMapper.writeValueAsString(tool), jacksonTypeRef()) + + assertThat(roundtrippedTool).isEqualTo(tool) + } + @Test fun ofFunction() { val function = @@ -31,6 +85,7 @@ internal class ToolTest { .build() ) .strict(true) + .description("description") .build() val tool = Tool.ofFunction(function) @@ -41,6 +96,29 @@ internal class ToolTest { assertThat(tool.webSearch()).isEmpty } + @Test + fun ofFunctionRoundtrip() { + val jsonMapper = jsonMapper() + val tool = + Tool.ofFunction( + FunctionTool.builder() + .name("name") + .parameters( + FunctionTool.Parameters.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .strict(true) + .description("description") + .build() + ) + + val roundtrippedTool = + jsonMapper.readValue(jsonMapper.writeValueAsString(tool), jacksonTypeRef()) + + assertThat(roundtrippedTool).isEqualTo(tool) + } + @Test fun ofComputerUsePreview() { val computerUsePreview = @@ -58,9 +136,39 @@ internal class ToolTest { assertThat(tool.webSearch()).isEmpty } + @Test + fun ofComputerUsePreviewRoundtrip() { + val jsonMapper = jsonMapper() + val tool = + Tool.ofComputerUsePreview( + ComputerTool.builder() + .displayHeight(0.0) + .displayWidth(0.0) + .environment(ComputerTool.Environment.MAC) + .build() + ) + + val roundtrippedTool = + jsonMapper.readValue(jsonMapper.writeValueAsString(tool), jacksonTypeRef()) + + assertThat(roundtrippedTool).isEqualTo(tool) + } + @Test fun ofWebSearch() { - val webSearch = WebSearchTool.builder().type(WebSearchTool.Type.WEB_SEARCH_PREVIEW).build() + val webSearch = + WebSearchTool.builder() + .type(WebSearchTool.Type.WEB_SEARCH_PREVIEW) + .searchContextSize(WebSearchTool.SearchContextSize.LOW) + .userLocation( + WebSearchTool.UserLocation.builder() + .city("city") + .country("country") + .region("region") + .timezone("timezone") + .build() + ) + .build() val tool = Tool.ofWebSearch(webSearch) @@ -69,4 +177,46 @@ internal class ToolTest { assertThat(tool.computerUsePreview()).isEmpty assertThat(tool.webSearch()).contains(webSearch) } + + @Test + fun ofWebSearchRoundtrip() { + val jsonMapper = jsonMapper() + val tool = + Tool.ofWebSearch( + WebSearchTool.builder() + .type(WebSearchTool.Type.WEB_SEARCH_PREVIEW) + .searchContextSize(WebSearchTool.SearchContextSize.LOW) + .userLocation( + WebSearchTool.UserLocation.builder() + .city("city") + .country("country") + .region("region") + .timezone("timezone") + .build() + ) + .build() + ) + + val roundtrippedTool = + jsonMapper.readValue(jsonMapper.writeValueAsString(tool), jacksonTypeRef()) + + assertThat(roundtrippedTool).isEqualTo(tool) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val tool = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { tool.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/WebSearchToolTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/WebSearchToolTest.kt index 8dde89d53..e7eb31c6b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/WebSearchToolTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/WebSearchToolTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -35,4 +37,30 @@ internal class WebSearchToolTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val webSearchTool = + WebSearchTool.builder() + .type(WebSearchTool.Type.WEB_SEARCH_PREVIEW) + .searchContextSize(WebSearchTool.SearchContextSize.LOW) + .userLocation( + WebSearchTool.UserLocation.builder() + .city("city") + .country("country") + .region("region") + .timezone("timezone") + .build() + ) + .build() + + val roundtrippedWebSearchTool = + jsonMapper.readValue( + jsonMapper.writeValueAsString(webSearchTool), + jacksonTypeRef(), + ) + + assertThat(roundtrippedWebSearchTool).isEqualTo(webSearchTool) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/inputitems/ResponseItemListTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/inputitems/ResponseItemListTest.kt index 676914263..e5cd6b8f2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/inputitems/ResponseItemListTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/inputitems/ResponseItemListTest.kt @@ -2,6 +2,8 @@ package com.openai.models.responses.inputitems +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.responses.ResponseInputMessageItem import com.openai.models.responses.ResponseItem import org.assertj.core.api.Assertions.assertThat @@ -43,4 +45,32 @@ internal class ResponseItemListTest { assertThat(responseItemList.hasMore()).isEqualTo(true) assertThat(responseItemList.lastId()).isEqualTo("last_id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseItemList = + ResponseItemList.builder() + .addData( + ResponseInputMessageItem.builder() + .id("id") + .addInputTextContent("text") + .role(ResponseInputMessageItem.Role.USER) + .status(ResponseInputMessageItem.Status.IN_PROGRESS) + .type(ResponseInputMessageItem.Type.MESSAGE) + .build() + ) + .firstId("first_id") + .hasMore(true) + .lastId("last_id") + .build() + + val roundtrippedResponseItemList = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseItemList), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseItemList).isEqualTo(responseItemList) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/uploads/UploadTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/uploads/UploadTest.kt index f216f2898..218d52b1c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/uploads/UploadTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/uploads/UploadTest.kt @@ -2,6 +2,8 @@ package com.openai.models.uploads +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import com.openai.models.files.FileObject import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -54,4 +56,36 @@ internal class UploadTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val upload = + Upload.builder() + .id("id") + .bytes(0L) + .createdAt(0L) + .expiresAt(0L) + .filename("filename") + .purpose("purpose") + .status(Upload.Status.PENDING) + .file( + FileObject.builder() + .id("id") + .bytes(0L) + .createdAt(0L) + .filename("filename") + .purpose(FileObject.Purpose.ASSISTANTS) + .status(FileObject.Status.UPLOADED) + .expiresAt(0L) + .statusDetails("status_details") + .build() + ) + .build() + + val roundtrippedUpload = + jsonMapper.readValue(jsonMapper.writeValueAsString(upload), jacksonTypeRef()) + + assertThat(roundtrippedUpload).isEqualTo(upload) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/uploads/parts/UploadPartTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/uploads/parts/UploadPartTest.kt index 059b64659..54d6f5f35 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/uploads/parts/UploadPartTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/uploads/parts/UploadPartTest.kt @@ -2,6 +2,8 @@ package com.openai.models.uploads.parts +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -15,4 +17,18 @@ internal class UploadPartTest { assertThat(uploadPart.createdAt()).isEqualTo(0L) assertThat(uploadPart.uploadId()).isEqualTo("upload_id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val uploadPart = UploadPart.builder().id("id").createdAt(0L).uploadId("upload_id").build() + + val roundtrippedUploadPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(uploadPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUploadPart).isEqualTo(uploadPart) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParamTest.kt index 3738c7a6f..f66f61956 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/AutoFileChunkingStrategyParamTest.kt @@ -2,6 +2,9 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class AutoFileChunkingStrategyParamTest { @@ -10,4 +13,19 @@ internal class AutoFileChunkingStrategyParamTest { fun create() { val autoFileChunkingStrategyParam = AutoFileChunkingStrategyParam.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val autoFileChunkingStrategyParam = AutoFileChunkingStrategyParam.builder().build() + + val roundtrippedAutoFileChunkingStrategyParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(autoFileChunkingStrategyParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAutoFileChunkingStrategyParam) + .isEqualTo(autoFileChunkingStrategyParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParamTest.kt index e2d017ee5..66f08ddd0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyParamTest.kt @@ -2,8 +2,15 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class FileChunkingStrategyParamTest { @@ -17,6 +24,21 @@ internal class FileChunkingStrategyParamTest { assertThat(fileChunkingStrategyParam.static_()).isEmpty } + @Test + fun ofAutoRoundtrip() { + val jsonMapper = jsonMapper() + val fileChunkingStrategyParam = + FileChunkingStrategyParam.ofAuto(AutoFileChunkingStrategyParam.builder().build()) + + val roundtrippedFileChunkingStrategyParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileChunkingStrategyParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileChunkingStrategyParam).isEqualTo(fileChunkingStrategyParam) + } + @Test fun ofStatic() { val static_ = @@ -34,4 +56,46 @@ internal class FileChunkingStrategyParamTest { assertThat(fileChunkingStrategyParam.auto()).isEmpty assertThat(fileChunkingStrategyParam.static_()).contains(static_) } + + @Test + fun ofStaticRoundtrip() { + val jsonMapper = jsonMapper() + val fileChunkingStrategyParam = + FileChunkingStrategyParam.ofStatic( + StaticFileChunkingStrategyObjectParam.builder() + .static_( + StaticFileChunkingStrategy.builder() + .chunkOverlapTokens(0L) + .maxChunkSizeTokens(100L) + .build() + ) + .build() + ) + + val roundtrippedFileChunkingStrategyParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileChunkingStrategyParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileChunkingStrategyParam).isEqualTo(fileChunkingStrategyParam) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val fileChunkingStrategyParam = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { fileChunkingStrategyParam.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyTest.kt index 43ae8a642..31d0fc541 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/FileChunkingStrategyTest.kt @@ -2,8 +2,15 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class FileChunkingStrategyTest { @@ -25,6 +32,30 @@ internal class FileChunkingStrategyTest { assertThat(fileChunkingStrategy.other()).isEmpty } + @Test + fun ofStaticRoundtrip() { + val jsonMapper = jsonMapper() + val fileChunkingStrategy = + FileChunkingStrategy.ofStatic( + StaticFileChunkingStrategyObject.builder() + .static_( + StaticFileChunkingStrategy.builder() + .chunkOverlapTokens(0L) + .maxChunkSizeTokens(100L) + .build() + ) + .build() + ) + + val roundtrippedFileChunkingStrategy = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileChunkingStrategy), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileChunkingStrategy).isEqualTo(fileChunkingStrategy) + } + @Test fun ofOther() { val other = OtherFileChunkingStrategyObject.builder().build() @@ -34,4 +65,37 @@ internal class FileChunkingStrategyTest { assertThat(fileChunkingStrategy.static_()).isEmpty assertThat(fileChunkingStrategy.other()).contains(other) } + + @Test + fun ofOtherRoundtrip() { + val jsonMapper = jsonMapper() + val fileChunkingStrategy = + FileChunkingStrategy.ofOther(OtherFileChunkingStrategyObject.builder().build()) + + val roundtrippedFileChunkingStrategy = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileChunkingStrategy), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileChunkingStrategy).isEqualTo(fileChunkingStrategy) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val fileChunkingStrategy = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { fileChunkingStrategy.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObjectTest.kt index e4e5f5227..36f15b61e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/OtherFileChunkingStrategyObjectTest.kt @@ -2,6 +2,9 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class OtherFileChunkingStrategyObjectTest { @@ -10,4 +13,19 @@ internal class OtherFileChunkingStrategyObjectTest { fun create() { val otherFileChunkingStrategyObject = OtherFileChunkingStrategyObject.builder().build() } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val otherFileChunkingStrategyObject = OtherFileChunkingStrategyObject.builder().build() + + val roundtrippedOtherFileChunkingStrategyObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(otherFileChunkingStrategyObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedOtherFileChunkingStrategyObject) + .isEqualTo(otherFileChunkingStrategyObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParamTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParamTest.kt index 83297b1e9..88a38f68f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParamTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectParamTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -27,4 +29,27 @@ internal class StaticFileChunkingStrategyObjectParamTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val staticFileChunkingStrategyObjectParam = + StaticFileChunkingStrategyObjectParam.builder() + .static_( + StaticFileChunkingStrategy.builder() + .chunkOverlapTokens(0L) + .maxChunkSizeTokens(100L) + .build() + ) + .build() + + val roundtrippedStaticFileChunkingStrategyObjectParam = + jsonMapper.readValue( + jsonMapper.writeValueAsString(staticFileChunkingStrategyObjectParam), + jacksonTypeRef(), + ) + + assertThat(roundtrippedStaticFileChunkingStrategyObjectParam) + .isEqualTo(staticFileChunkingStrategyObjectParam) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectTest.kt index 3e9c1ec42..4f4e5c15b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyObjectTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -27,4 +29,27 @@ internal class StaticFileChunkingStrategyObjectTest { .build() ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val staticFileChunkingStrategyObject = + StaticFileChunkingStrategyObject.builder() + .static_( + StaticFileChunkingStrategy.builder() + .chunkOverlapTokens(0L) + .maxChunkSizeTokens(100L) + .build() + ) + .build() + + val roundtrippedStaticFileChunkingStrategyObject = + jsonMapper.readValue( + jsonMapper.writeValueAsString(staticFileChunkingStrategyObject), + jacksonTypeRef(), + ) + + assertThat(roundtrippedStaticFileChunkingStrategyObject) + .isEqualTo(staticFileChunkingStrategyObject) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyTest.kt index 0491b6847..461cb20a4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/StaticFileChunkingStrategyTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -18,4 +20,22 @@ internal class StaticFileChunkingStrategyTest { assertThat(staticFileChunkingStrategy.chunkOverlapTokens()).isEqualTo(0L) assertThat(staticFileChunkingStrategy.maxChunkSizeTokens()).isEqualTo(100L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val staticFileChunkingStrategy = + StaticFileChunkingStrategy.builder() + .chunkOverlapTokens(0L) + .maxChunkSizeTokens(100L) + .build() + + val roundtrippedStaticFileChunkingStrategy = + jsonMapper.readValue( + jsonMapper.writeValueAsString(staticFileChunkingStrategy), + jacksonTypeRef(), + ) + + assertThat(roundtrippedStaticFileChunkingStrategy).isEqualTo(staticFileChunkingStrategy) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreDeletedTest.kt index 98903ef98..a3c86c8a2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class VectorStoreDeletedTest { assertThat(vectorStoreDeleted.id()).isEqualTo("id") assertThat(vectorStoreDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val vectorStoreDeleted = VectorStoreDeleted.builder().id("id").deleted(true).build() + + val roundtrippedVectorStoreDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(vectorStoreDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVectorStoreDeleted).isEqualTo(vectorStoreDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponseTest.kt index 17a6b0658..0d896e1ad 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreSearchResponseTest.kt @@ -2,7 +2,9 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -45,4 +47,34 @@ internal class VectorStoreSearchResponseTest { assertThat(vectorStoreSearchResponse.filename()).isEqualTo("filename") assertThat(vectorStoreSearchResponse.score()).isEqualTo(0.0) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val vectorStoreSearchResponse = + VectorStoreSearchResponse.builder() + .attributes( + VectorStoreSearchResponse.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .addContent( + VectorStoreSearchResponse.Content.builder() + .text("text") + .type(VectorStoreSearchResponse.Content.Type.TEXT) + .build() + ) + .fileId("file_id") + .filename("filename") + .score(0.0) + .build() + + val roundtrippedVectorStoreSearchResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(vectorStoreSearchResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVectorStoreSearchResponse).isEqualTo(vectorStoreSearchResponse) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreTest.kt index 764895a5b..8dd20a14c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/VectorStoreTest.kt @@ -2,7 +2,9 @@ package com.openai.models.vectorstores +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -62,4 +64,42 @@ internal class VectorStoreTest { .contains(VectorStore.ExpiresAfter.builder().days(1L).build()) assertThat(vectorStore.expiresAt()).contains(0L) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val vectorStore = + VectorStore.builder() + .id("id") + .createdAt(0L) + .fileCounts( + VectorStore.FileCounts.builder() + .cancelled(0L) + .completed(0L) + .failed(0L) + .inProgress(0L) + .total(0L) + .build() + ) + .lastActiveAt(0L) + .metadata( + VectorStore.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .name("name") + .status(VectorStore.Status.EXPIRED) + .usageBytes(0L) + .expiresAfter(VectorStore.ExpiresAfter.builder().days(1L).build()) + .expiresAt(0L) + .build() + + val roundtrippedVectorStore = + jsonMapper.readValue( + jsonMapper.writeValueAsString(vectorStore), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVectorStore).isEqualTo(vectorStore) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatchTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatchTest.kt index bb58e6cd1..8275818ca 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatchTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/filebatches/VectorStoreFileBatchTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores.filebatches +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -41,4 +43,33 @@ internal class VectorStoreFileBatchTest { assertThat(vectorStoreFileBatch.status()).isEqualTo(VectorStoreFileBatch.Status.IN_PROGRESS) assertThat(vectorStoreFileBatch.vectorStoreId()).isEqualTo("vector_store_id") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val vectorStoreFileBatch = + VectorStoreFileBatch.builder() + .id("id") + .createdAt(0L) + .fileCounts( + VectorStoreFileBatch.FileCounts.builder() + .cancelled(0L) + .completed(0L) + .failed(0L) + .inProgress(0L) + .total(0L) + .build() + ) + .status(VectorStoreFileBatch.Status.IN_PROGRESS) + .vectorStoreId("vector_store_id") + .build() + + val roundtrippedVectorStoreFileBatch = + jsonMapper.readValue( + jsonMapper.writeValueAsString(vectorStoreFileBatch), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVectorStoreFileBatch).isEqualTo(vectorStoreFileBatch) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/FileContentResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/FileContentResponseTest.kt index 6f405c530..8629b74ee 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/FileContentResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/FileContentResponseTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores.files +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class FileContentResponseTest { assertThat(fileContentResponse.text()).contains("text") assertThat(fileContentResponse.type()).contains("type") } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fileContentResponse = FileContentResponse.builder().text("text").type("type").build() + + val roundtrippedFileContentResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fileContentResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFileContentResponse).isEqualTo(fileContentResponse) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeletedTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeletedTest.kt index 5b3174566..520dc030f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeletedTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileDeletedTest.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores.files +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -14,4 +16,18 @@ internal class VectorStoreFileDeletedTest { assertThat(vectorStoreFileDeleted.id()).isEqualTo("id") assertThat(vectorStoreFileDeleted.deleted()).isEqualTo(true) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val vectorStoreFileDeleted = VectorStoreFileDeleted.builder().id("id").deleted(true).build() + + val roundtrippedVectorStoreFileDeleted = + jsonMapper.readValue( + jsonMapper.writeValueAsString(vectorStoreFileDeleted), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVectorStoreFileDeleted).isEqualTo(vectorStoreFileDeleted) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileTest.kt index f3eff7a00..6889fc7b0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/vectorstores/files/VectorStoreFileTest.kt @@ -2,7 +2,9 @@ package com.openai.models.vectorstores.files +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue +import com.openai.core.jsonMapper import com.openai.models.vectorstores.FileChunkingStrategy import com.openai.models.vectorstores.StaticFileChunkingStrategy import com.openai.models.vectorstores.StaticFileChunkingStrategyObject @@ -71,4 +73,42 @@ internal class VectorStoreFileTest { ) ) } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val vectorStoreFile = + VectorStoreFile.builder() + .id("id") + .createdAt(0L) + .lastError( + VectorStoreFile.LastError.builder() + .code(VectorStoreFile.LastError.Code.SERVER_ERROR) + .message("message") + .build() + ) + .status(VectorStoreFile.Status.IN_PROGRESS) + .usageBytes(0L) + .vectorStoreId("vector_store_id") + .attributes( + VectorStoreFile.Attributes.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .staticChunkingStrategy( + StaticFileChunkingStrategy.builder() + .chunkOverlapTokens(0L) + .maxChunkSizeTokens(100L) + .build() + ) + .build() + + val roundtrippedVectorStoreFile = + jsonMapper.readValue( + jsonMapper.writeValueAsString(vectorStoreFile), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVectorStoreFile).isEqualTo(vectorStoreFile) + } }