diff --git a/.castiron.stats.yml b/.castiron.stats.yml index b9a5be7b1..b479e5def 100644 --- a/.castiron.stats.yml +++ b/.castiron.stats.yml @@ -1,8 +1,8 @@ schema_version: 1 -generation_id: 7b8e4f9d-66bd-465d-97d2-cbe0f41aadaf -openapi_spec_hash: 28504532afa0c3f477f005e641ec7d6d -openapi_transformed_spec_hash: 13422b63a47a5d25533cf55573de7632 -config_hash: 4446d9cc8e7a00d29ce72282aa5616f9 -codegen_sha: b231d2133bce32566450e5544a3205825be270b7 -codegen_hash: bafe248e46487a4eb8ea6868d7fa57e6453e489bf7522869c0ad8eb8125e247d -public_codegen_sha: bb32fa596d2a5e59ed4b7a908aebcdf347cced9d +generation_id: 13866b42-3537-41d4-9c12-f53214868c28 +openapi_spec_hash: 4a02922518092801c43f2526a9ced0f5 +openapi_transformed_spec_hash: b577364c156fa7f501df6fef87ae11ed +config_hash: 07f8531f9ff20bc9f5156b5a258c76ff +codegen_sha: e92d3226b583127c9519d4b89e52c20d6c596d0b +codegen_hash: 7afac60a97214c8b2cd5a6684367ee3e19efa7bd30856d197a78779278534669 +public_codegen_sha: cf08e5bb49e8c3d1f4eadee5d4b517dd2a06cabc diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml index 4daf717a0..7e2702a43 100644 --- a/.github/workflows/castiron-custom-code-comment.yml +++ b/.github/workflows/castiron-custom-code-comment.yml @@ -70,6 +70,24 @@ jobs: policy.max_custom_patch_lines < 0n) throw new Error('Invalid policy budget'); return policy; } + async function supportsEffectivePinReview(ref) { + let file; + try { + ({data: file} = await github.rest.repos.getContent({ + ...context.repo, path: 'scripts/castiron/custom_code_policy.py', ref, + })); + } catch (error) { + if (error.status === 404) return false; + throw error; + } + if (file.type !== 'file' || file.encoding !== 'base64' || !Number.isSafeInteger(file.size) || + file.size < 0 || typeof file.content !== 'string') throw new Error('Invalid checker contract'); + const content = Buffer.from(file.content, 'base64'); + if (content.length !== file.size) throw new Error('Invalid checker content'); + // Read data only from the effective checker, never import or execute it. + const declarations = content.toString('utf8').match(/^PIN_REVIEW_POLICY = .*$/gm) || []; + return declarations.length === 1 && declarations[0] === 'PIN_REVIEW_POLICY = "effective-policy-v1"'; + } async function pinRequiresHuman(pr, headPolicy, mainSha) { const targetPolicy = await readPolicy(pr.base.sha); const pinSha = pin => pin?.repository === repo && /^[0-9a-f]{40}$/.test(pin.main_sha || '') @@ -78,6 +96,9 @@ jobs: const previousChecker = targetPolicy.next_policy ? pinSha(targetPolicy.next_policy) : mainSha; if (!proposedChecker || !/^[0-9a-f]{40}$/.test(previousChecker || '') || !/^[0-9a-f]{40}$/.test(mainSha || '')) return true; + // Older checkers require human review for every pin edit. Retain that + // requirement until the protected checker explicitly supports the exemption. + if (!await supportsEffectivePinReview(previousChecker)) return true; const [previous, proposed, previousBase, proposedBase] = await Promise.all([ readPolicy(previousChecker), readPolicy(proposedChecker), mergeBase(previousChecker, proposedChecker), diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml index 573d7fb27..01f2e5157 100644 --- a/api_reference/openapi.transformed.yml +++ b/api_reference/openapi.transformed.yml @@ -82302,21 +82302,24 @@ components: DecisionInputContentParts: type: array items: - oneOf: - - $ref: '#/components/schemas/DecisionInputText' - - $ref: '#/components/schemas/DecisionInputImage' + x-oai-discriminator-values: + - input_text + - input_image + - input_json discriminator: propertyName: type mapping: input_text: '#/components/schemas/DecisionInputText' input_image: '#/components/schemas/DecisionInputImage' - x-oai-discriminator-values: - - input_text - - input_image + input_json: '#/components/schemas/DecisionInputJson' + anyOf: + - $ref: '#/components/schemas/DecisionInputText' + - $ref: '#/components/schemas/DecisionInputImage' + - $ref: '#/components/schemas/DecisionInputJson' minItems: 0 maxItems: 16384 DecisionInputContent: - description: Text evidence or an ordered list of text and image parts. + description: A text string or an ordered list of text, image, and JSON parts. anyOf: - type: string minLength: 0 @@ -82330,26 +82333,23 @@ components: - final_answer DecisionInputMessage: type: object - properties: - role: - type: string - enum: - - user - default: user - x-stainless-const: true - content: - $ref: '#/components/schemas/DecisionInputContent' - type: - type: string - enum: - - message - default: message - x-stainless-const: true - required: - - role - - content - additionalProperties: false - description: A user message containing text or images. + discriminator: + propertyName: role + mapping: + user: '#/components/schemas/DecisionInputUserMessage' + developer: '#/components/schemas/DecisionInputDeveloperMessage' + system: '#/components/schemas/DecisionInputSystemMessage' + assistant: '#/components/schemas/DecisionInputAssistantMessage' + x-oai-discriminator-values: + - user + - developer + - system + - assistant + anyOf: + - $ref: '#/components/schemas/DecisionInputUserMessage' + - $ref: '#/components/schemas/DecisionInputDeveloperMessage' + - $ref: '#/components/schemas/DecisionInputSystemMessage' + - $ref: '#/components/schemas/DecisionInputAssistantMessage' FunctionCallStatusParam: type: string enum: @@ -82531,24 +82531,36 @@ components: - type additionalProperties: false DecisionInput: - oneOf: + description: The input to evaluate for every question. Provide a text string or an ordered array of messages, function calls and outputs, or custom tool calls and outputs. Each tool output must identify a matching call by call ID. + anyOf: - type: string minLength: 0 maxLength: 10485760 - type: array items: - oneOf: - - $ref: '#/components/schemas/DecisionInputMessage' + x-oai-default-discriminator-value: message discriminator: propertyName: type mapping: message: '#/components/schemas/DecisionInputMessage' + function_call: '#/components/schemas/DecisionInputFunctionCall' + function_call_output: '#/components/schemas/DecisionInputFunctionCallOutput' + custom_tool_call: '#/components/schemas/DecisionInputCustomToolCall' + custom_tool_call_output: '#/components/schemas/DecisionInputCustomToolCallOutput' x-oai-discriminator-values: - message - x-oai-default-discriminator-value: message + - function_call + - function_call_output + - custom_tool_call + - custom_tool_call_output + anyOf: + - $ref: '#/components/schemas/DecisionInputMessage' + - $ref: '#/components/schemas/DecisionInputFunctionCall' + - $ref: '#/components/schemas/DecisionInputFunctionCallOutput' + - $ref: '#/components/schemas/DecisionInputCustomToolCall' + - $ref: '#/components/schemas/DecisionInputCustomToolCallOutput' minItems: 0 maxItems: 131072 - description: The text or images to evaluate for every question. Provide a text string or user messages containing text and images. Images can be base64 data URLs or publicly accessible HTTP(S) URLs; at most 128 images are allowed across all messages in one request. Files, audio, tools, and item references are not supported. QuestionParamPredicate: type: object properties: @@ -82564,9 +82576,15 @@ components: minLength: 0 maxLength: 1048576 instructions: - type: string - minLength: 0 - maxLength: 1048576 + anyOf: + - type: string + minLength: 0 + maxLength: 1048576 + - type: array + items: + $ref: '#/components/schemas/DecisionQuestionInputPart' + minItems: 1 + maxItems: 16384 required: - type - instructions @@ -82606,9 +82624,15 @@ components: minLength: 0 maxLength: 1048576 instructions: - type: string - minLength: 0 - maxLength: 1048576 + anyOf: + - type: string + minLength: 0 + maxLength: 1048576 + - type: array + items: + $ref: '#/components/schemas/DecisionQuestionInputPart' + minItems: 1 + maxItems: 16384 choices: type: array items: @@ -82651,9 +82675,15 @@ components: minLength: 0 maxLength: 1048576 instructions: - type: string - minLength: 0 - maxLength: 1048576 + anyOf: + - type: string + minLength: 0 + maxLength: 1048576 + - type: array + items: + $ref: '#/components/schemas/DecisionQuestionInputPart' + minItems: 1 + maxItems: 16384 levels: type: array items: @@ -82697,8 +82727,8 @@ components: items: $ref: '#/components/schemas/QuestionParam' minItems: 1 - maxItems: 200 - description: Ask 1–200 questions about the input. Each question can be a predicate, choice, or score. Answers come back in the same order. + maxItems: 400 + description: Ask 1–400 questions about the input. Each question can be a predicate, choice, or score. Answers come back in the same order. safety_identifier: type: - string @@ -107532,6 +107562,233 @@ components: "object": "audio.voice.deleted", "deleted": true } + DecisionInputJson: + type: object + properties: + type: + type: string + enum: + - input_json + default: input_json + x-stainless-const: true + data: + anyOf: + - type: object + additionalProperties: + anyOf: + - {} + - type: 'null' + propertyNames: + type: string + minLength: 0 + minProperties: 0 + - type: array + items: + type: object + additionalProperties: + anyOf: + - {} + - type: 'null' + propertyNames: + type: string + minLength: 0 + minProperties: 0 + minItems: 0 + required: + - type + - data + additionalProperties: false + description: A JSON object or an array of objects rendered as text. Nested values may be any JSON value. Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + DecisionQuestionInputPart: + description: A text, image, or JSON content part in a question instruction. + discriminator: + propertyName: type + mapping: + input_text: '#/components/schemas/DecisionQuestionInputText' + input_image: '#/components/schemas/DecisionQuestionInputImage' + input_json: '#/components/schemas/DecisionInputJson' + x-oai-discriminator-values: + - input_text + - input_image + - input_json + anyOf: + - $ref: '#/components/schemas/DecisionQuestionInputText' + - $ref: '#/components/schemas/DecisionQuestionInputImage' + - $ref: '#/components/schemas/DecisionInputJson' + DecisionQuestionInputText: + type: object + properties: + type: + type: string + enum: + - input_text + default: input_text + x-stainless-const: true + text: + type: string + minLength: 0 + maxLength: 10485760 + required: + - type + - text + additionalProperties: false + DecisionQuestionInputImage: + type: object + properties: + image_url: + type: string + minLength: 0 + maxLength: 1073741824 + pattern: ^(data:|https?://) + description: A base64-encoded image in a data URL or a publicly accessible HTTP(S) image URL. + detail: + anyOf: + - $ref: '#/components/schemas/ImageDetailParam' + - type: 'null' + description: The image detail level, using the selected model's image profile. Defaults to auto. + default: auto + type: + type: string + enum: + - input_image + default: input_image + x-stainless-const: true + required: + - image_url + - type + additionalProperties: false + description: An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are not supported. + DecisionInputUserMessage: + type: object + properties: + role: + type: string + enum: + - user + default: user + x-stainless-const: true + content: + $ref: '#/components/schemas/DecisionInputContent' + type: + type: string + enum: + - message + default: message + x-stainless-const: true + required: + - role + - content + additionalProperties: false + description: A user message containing input text, images, or JSON. + DecisionInputDeveloperMessage: + type: object + properties: + role: + type: string + enum: + - developer + default: developer + x-stainless-const: true + content: + $ref: '#/components/schemas/DecisionInputDeveloperContent' + type: + type: string + enum: + - message + default: message + x-stainless-const: true + required: + - role + - content + additionalProperties: false + description: A developer message containing input text. + DecisionInputSystemMessage: + type: object + properties: + role: + type: string + enum: + - system + default: system + x-stainless-const: true + content: + $ref: '#/components/schemas/DecisionInputDeveloperContent' + type: + type: string + enum: + - message + default: message + x-stainless-const: true + required: + - role + - content + additionalProperties: false + description: A system message containing input text. + DecisionInputAssistantMessage: + type: object + properties: + role: + type: string + enum: + - assistant + default: assistant + x-stainless-const: true + content: + $ref: '#/components/schemas/DecisionInputAssistantContent' + phase: + anyOf: + - $ref: '#/components/schemas/MessagePhaseParam' + - type: 'null' + description: The phase of an assistant message. + type: + type: string + enum: + - message + default: message + x-stainless-const: true + required: + - role + - content + additionalProperties: false + description: An assistant message containing output text or refusals. + DecisionInputDeveloperContent: + description: A text string or an ordered list of input text parts. + anyOf: + - type: string + minLength: 0 + maxLength: 10485760 + - $ref: '#/components/schemas/DecisionInputDeveloperContentParts' + title: Parts + DecisionInputAssistantContent: + description: A text string or an ordered list of output text and refusal parts. + anyOf: + - type: string + minLength: 0 + maxLength: 10485760 + - $ref: '#/components/schemas/DecisionInputAssistantContentParts' + title: Parts + DecisionInputDeveloperContentParts: + type: array + items: + $ref: '#/components/schemas/DecisionInputText' + minItems: 0 + maxItems: 16384 + DecisionInputAssistantContentParts: + type: array + items: + x-oai-discriminator-values: + - output_text + - refusal + discriminator: + propertyName: type + mapping: + output_text: '#/components/schemas/DecisionInputOutputText' + refusal: '#/components/schemas/DecisionInputRefusal' + anyOf: + - $ref: '#/components/schemas/DecisionInputOutputText' + - $ref: '#/components/schemas/DecisionInputRefusal' + minItems: 0 + maxItems: 16384 SessionSpendControlResource: type: object properties: diff --git a/openai-java-core/src/main/kotlin/com/openai/models/DecisionInputJson.kt b/openai-java-core/src/main/kotlin/com/openai/models/DecisionInputJson.kt new file mode 100644 index 000000000..d87f12b9b --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/DecisionInputJson.kt @@ -0,0 +1,601 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.BaseDeserializer +import com.openai.core.BaseSerializer +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.collectAllProperties +import com.openai.core.getOrThrow +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON value. + * Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ +class DecisionInputJson +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(data, type, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): Data = data.getRequired("data") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("input_json") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties(additionalProperties, "data" to this.data, "type" to this.type) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. This + * accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [DecisionInputJson]. + * + * The following fields are required: + * ```java + * .data() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DecisionInputJson]. */ + class Builder internal constructor() { + + private var data: JsonField? = null + private var type: JsonValue = JsonValue.from("input_json") + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(decisionInputJson: DecisionInputJson) = apply { + data = decisionInputJson.data + type = decisionInputJson.type + additionalProperties = decisionInputJson.additionalProperties.toMutableMap() + } + + fun data(data: Data) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField) = apply { this.data = data } + + /** Alias for calling [data] with `Data.ofUnionMember0(unionMember0)`. */ + fun data(unionMember0: Data.UnionMember0) = data(Data.ofUnionMember0(unionMember0)) + + /** Alias for calling [data] with `Data.ofMaps(maps)`. */ + fun dataOfMaps(maps: List>) = data(Data.ofMaps(maps)) + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("input_json") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [DecisionInputJson]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .data() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): DecisionInputJson = + DecisionInputJson( + checkRequired("data", data), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): DecisionInputJson = apply { + if (validated) { + return@apply + } + + data().validate() + _type().let { + if (it != JsonValue.from("input_json")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (data.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("input_json")) 1 else 0 } + + @JsonDeserialize(using = Data.Deserializer::class) + @JsonSerialize(using = Data.Serializer::class) + class Data + private constructor( + private val unionMember0: UnionMember0? = null, + maps: List>? = null, + private val _json: JsonValue? = null, + ) { + + private val maps = maps?.map { it.toImmutable() }?.toImmutable() + + fun unionMember0(): Optional = Optional.ofNullable(unionMember0) + + fun maps(): Optional>> = Optional.ofNullable(maps) + + fun isUnionMember0(): Boolean = unionMember0 != null + + fun isMaps(): Boolean = maps != null + + fun asUnionMember0(): UnionMember0 = unionMember0.getOrThrow("unionMember0") + + fun asMaps(): List> = maps.getOrThrow("maps") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = data.accept(new Data.Visitor>() { + * @Override + * public Optional visitUnionMember0(UnionMember0 unionMember0) { + * return Optional.of(unionMember0.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in [visitor] + * and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + unionMember0 != null -> visitor.visitUnionMember0(unionMember0) + maps != null -> visitor.visitMaps(maps) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Data = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitUnionMember0(unionMember0: UnionMember0) { + unionMember0.validate() + } + + override fun visitMaps(maps: List>) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns 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 visitUnionMember0(unionMember0: UnionMember0) = + unionMember0.validity() + + override fun visitMaps(maps: List>) = maps.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && unionMember0 == other.unionMember0 && maps == other.maps + } + + override fun hashCode(): Int = Objects.hash(unionMember0, maps) + + override fun toString(): String = + when { + unionMember0 != null -> "Data{unionMember0=$unionMember0}" + maps != null -> "Data{maps=$maps}" + _json != null -> "Data{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Data") + } + + companion object { + + @JvmStatic + fun ofUnionMember0(unionMember0: UnionMember0) = Data(unionMember0 = unionMember0) + + @JvmStatic fun ofMaps(maps: List>) = Data(maps = maps) + } + + /** An interface that defines how to map each variant of [Data] to a value of type [T]. */ + interface Visitor { + + fun visitUnionMember0(unionMember0: UnionMember0): T + + fun visitMaps(maps: List>): T + + /** + * Maps an unknown variant of [Data] to a value of type [T]. + * + * An instance of [Data] can contain an unknown variant if it was deserialized from data + * that doesn't match any known variant. For example, if the SDK is on an older version + * than the API, then the API may respond with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Data") + } + } + + internal class Deserializer : BaseDeserializer(Data::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Data { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Data(unionMember0 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>>()) + ?.let { Data(maps = 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 -> Data(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Data::class) { + + override fun serialize( + value: Data, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.unionMember0 != null -> generator.writeObject(value.unionMember0) + value.maps != null -> generator.writeObject(value.maps) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Data") + } + } + } + + class UnionMember0 + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional + * properties with the same name. Missing properties are omitted, while explicit nulls + * and unrecognized values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties(additionalProperties) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON + * properties. This accessor retains its legacy additional-properties behavior. + */ + @Deprecated( + "Use _allProperties() to access both declared and additional JSON properties." + ) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [UnionMember0]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember0]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember0: UnionMember0) = apply { + additionalProperties = unionMember0.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UnionMember0]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnionMember0 = UnionMember0(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): UnionMember0 = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns 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 + } + + return other is UnionMember0 && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "UnionMember0{additionalProperties=$additionalProperties}" + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DecisionInputJson && + data == other.data && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, type, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DecisionInputJson{data=$data, type=$type, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputImage.kt b/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputImage.kt new file mode 100644 index 000000000..8a7818887 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputImage.kt @@ -0,0 +1,435 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.Enum +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.checkRequired +import com.openai.core.collectAllProperties +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are not + * supported. + */ +class DecisionQuestionInputImage +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val imageUrl: JsonField, + private val type: JsonValue, + private val detail: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("image_url") @ExcludeMissing imageUrl: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") @ExcludeMissing detail: JsonField = JsonMissing.of(), + ) : this(imageUrl, type, detail, mutableMapOf()) + + /** + * A base64-encoded image in a data URL or a publicly accessible HTTP(S) image URL. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun imageUrl(): String = imageUrl.getRequired("image_url") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The image detail level, using the selected model's image profile. Defaults to auto. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun detail(): Optional = detail.getOptional("detail") + + /** + * Returns the raw JSON value of [imageUrl]. + * + * Unlike [imageUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("image_url") @ExcludeMissing fun _imageUrl(): JsonField = imageUrl + + /** + * Returns the raw JSON value of [detail]. + * + * Unlike [detail], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("detail") @ExcludeMissing fun _detail(): JsonField = detail + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "image_url" to this.imageUrl, + "type" to this.type, + "detail" to this.detail, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. This + * accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [DecisionQuestionInputImage]. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DecisionQuestionInputImage]. */ + class Builder internal constructor() { + + private var imageUrl: JsonField? = null + private var type: JsonValue = JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(decisionQuestionInputImage: DecisionQuestionInputImage) = apply { + imageUrl = decisionQuestionInputImage.imageUrl + type = decisionQuestionInputImage.type + detail = decisionQuestionInputImage.detail + additionalProperties = decisionQuestionInputImage.additionalProperties.toMutableMap() + } + + /** A base64-encoded image in a data URL or a publicly accessible HTTP(S) image URL. */ + fun imageUrl(imageUrl: String) = imageUrl(JsonField.of(imageUrl)) + + /** + * Sets [Builder.imageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.imageUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun imageUrl(imageUrl: JsonField) = apply { this.imageUrl = imageUrl } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** The image detail level, using the selected model's image profile. Defaults to auto. */ + fun detail(detail: Detail?) = detail(JsonField.ofNullable(detail)) + + /** Alias for calling [Builder.detail] with `detail.orElse(null)`. */ + fun detail(detail: Optional) = detail(detail.getOrNull()) + + /** + * Sets [Builder.detail] to an arbitrary JSON value. + * + * You should usually call [Builder.detail] with a well-typed [Detail] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun detail(detail: JsonField) = apply { this.detail = detail } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [DecisionQuestionInputImage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .imageUrl() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): DecisionQuestionInputImage = + DecisionQuestionInputImage( + checkRequired("imageUrl", imageUrl), + type, + detail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): DecisionQuestionInputImage = apply { + if (validated) { + return@apply + } + + imageUrl() + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + 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 (imageUrl.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("input_image")) 1 else 0 } + + (detail.asKnown().getOrNull()?.validity() ?: 0) + + /** The image detail level, using the selected model's image profile. Defaults to auto. */ + class Detail @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val LOW = of("low") + + @JvmField val HIGH = of("high") + + @JvmField val AUTO = of("auto") + + @JvmField val ORIGINAL = of("original") + + @JvmStatic fun of(value: String) = Detail(JsonField.of(value)) + } + + /** An enum containing [Detail]'s known values. */ + enum class Known { + LOW, + HIGH, + AUTO, + ORIGINAL, + } + + /** + * An enum containing [Detail]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Detail] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LOW, + HIGH, + AUTO, + ORIGINAL, + /** An enum member indicating that [Detail] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LOW -> Value.LOW + HIGH -> Value.HIGH + AUTO -> Value.AUTO + ORIGINAL -> Value.ORIGINAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + LOW -> Known.LOW + HIGH -> Known.HIGH + AUTO -> Known.AUTO + ORIGINAL -> Known.ORIGINAL + else -> throw OpenAIInvalidDataException("Unknown Detail: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + 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 + } + + return other is Detail && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DecisionQuestionInputImage && + imageUrl == other.imageUrl && + type == other.type && + detail == other.detail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(imageUrl, type, detail, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DecisionQuestionInputImage{imageUrl=$imageUrl, type=$type, detail=$detail, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputPart.kt b/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputPart.kt new file mode 100644 index 000000000..be14254e5 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputPart.kt @@ -0,0 +1,293 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +package com.openai.models + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +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.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** A text, image, or JSON content part in a question instruction. */ +@JsonDeserialize(using = DecisionQuestionInputPart.Deserializer::class) +@JsonSerialize(using = DecisionQuestionInputPart.Serializer::class) +class DecisionQuestionInputPart +private constructor( + private val inputText: DecisionQuestionInputText? = null, + private val inputImage: DecisionQuestionInputImage? = null, + private val inputJson: DecisionInputJson? = null, + private val _json: JsonValue? = null, +) { + + fun inputText(): Optional = Optional.ofNullable(inputText) + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are not + * supported. + */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON value. + * Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + fun inputJson(): Optional = Optional.ofNullable(inputJson) + + fun isInputText(): Boolean = inputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isInputJson(): Boolean = inputJson != null + + fun asInputText(): DecisionQuestionInputText = inputText.getOrThrow("inputText") + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are not + * supported. + */ + fun asInputImage(): DecisionQuestionInputImage = inputImage.getOrThrow("inputImage") + + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON value. + * Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + fun asInputJson(): DecisionInputJson = inputJson.getOrThrow("inputJson") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = decisionQuestionInputPart.accept(new DecisionQuestionInputPart.Visitor>() { + * @Override + * public Optional visitInputText(DecisionQuestionInputText inputText) { + * return Optional.of(inputText.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and + * the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + inputText != null -> visitor.visitInputText(inputText) + inputImage != null -> visitor.visitInputImage(inputImage) + inputJson != null -> visitor.visitInputJson(inputJson) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): DecisionQuestionInputPart = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitInputText(inputText: DecisionQuestionInputText) { + inputText.validate() + } + + override fun visitInputImage(inputImage: DecisionQuestionInputImage) { + inputImage.validate() + } + + override fun visitInputJson(inputJson: DecisionInputJson) { + inputJson.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 visitInputText(inputText: DecisionQuestionInputText) = + inputText.validity() + + override fun visitInputImage(inputImage: DecisionQuestionInputImage) = + inputImage.validity() + + override fun visitInputJson(inputJson: DecisionInputJson) = inputJson.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DecisionQuestionInputPart && + inputText == other.inputText && + inputImage == other.inputImage && + inputJson == other.inputJson + } + + override fun hashCode(): Int = Objects.hash(inputText, inputImage, inputJson) + + override fun toString(): String = + when { + inputText != null -> "DecisionQuestionInputPart{inputText=$inputText}" + inputImage != null -> "DecisionQuestionInputPart{inputImage=$inputImage}" + inputJson != null -> "DecisionQuestionInputPart{inputJson=$inputJson}" + _json != null -> "DecisionQuestionInputPart{_unknown=$_json}" + else -> throw IllegalStateException("Invalid DecisionQuestionInputPart") + } + + companion object { + + @JvmStatic + fun ofInputText(inputText: DecisionQuestionInputText) = + DecisionQuestionInputPart(inputText = inputText) + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are + * not supported. + */ + @JvmStatic + fun ofInputImage(inputImage: DecisionQuestionInputImage) = + DecisionQuestionInputPart(inputImage = inputImage) + + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON + * value. Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + @JvmStatic + fun ofInputJson(inputJson: DecisionInputJson) = + DecisionQuestionInputPart(inputJson = inputJson) + } + + /** + * An interface that defines how to map each variant of [DecisionQuestionInputPart] to a value + * of type [T]. + */ + interface Visitor { + + fun visitInputText(inputText: DecisionQuestionInputText): T + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are + * not supported. + */ + fun visitInputImage(inputImage: DecisionQuestionInputImage): T + + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON + * value. Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + fun visitInputJson(inputJson: DecisionInputJson): T + + /** + * Maps an unknown variant of [DecisionQuestionInputPart] to a value of type [T]. + * + * An instance of [DecisionQuestionInputPart] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown DecisionQuestionInputPart") + } + } + + internal class Deserializer : + BaseDeserializer(DecisionQuestionInputPart::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): DecisionQuestionInputPart { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "input_text" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionQuestionInputPart(inputText = it, _json = json) + } ?: DecisionQuestionInputPart(_json = json) + } + "input_image" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionQuestionInputPart(inputImage = it, _json = json) + } ?: DecisionQuestionInputPart(_json = json) + } + "input_json" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionQuestionInputPart(inputJson = it, _json = json) + } ?: DecisionQuestionInputPart(_json = json) + } + } + + return DecisionQuestionInputPart(_json = json) + } + } + + internal class Serializer : + BaseSerializer(DecisionQuestionInputPart::class) { + + override fun serialize( + value: DecisionQuestionInputPart, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.inputText != null -> generator.writeObject(value.inputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value.inputJson != null -> generator.writeObject(value.inputJson) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid DecisionQuestionInputPart") + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputText.kt b/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputText.kt new file mode 100644 index 000000000..8a5231674 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputText.kt @@ -0,0 +1,236 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.checkRequired +import com.openai.core.collectAllProperties +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects + +class DecisionQuestionInputText +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("text") @ExcludeMissing text: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun text(): String = text.getRequired("text") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("input_text") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties(additionalProperties, "text" to this.text, "type" to this.type) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. This + * accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [DecisionQuestionInputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DecisionQuestionInputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("input_text") + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(decisionQuestionInputText: DecisionQuestionInputText) = apply { + text = decisionQuestionInputText.text + type = decisionQuestionInputText.type + additionalProperties = decisionQuestionInputText.additionalProperties.toMutableMap() + } + + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("input_text") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [DecisionQuestionInputText]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): DecisionQuestionInputText = + DecisionQuestionInputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): DecisionQuestionInputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("input_text")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("input_text")) 1 else 0 } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DecisionQuestionInputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DecisionQuestionInputText{text=$text, type=$type, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionCreateParams.kt index 0bb59ead6..10a9bb9b3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionCreateParams.kt @@ -30,6 +30,7 @@ import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.DecisionQuestionInputPart import java.util.Collections import java.util.Objects import java.util.Optional @@ -47,10 +48,9 @@ private constructor( ) : Params { /** - * The text or images to evaluate for every question. Provide a text string or user messages - * containing text and images. Images can be base64 data URLs or publicly accessible HTTP(S) - * URLs; at most 128 images are allowed across all messages in one request. Files, audio, tools, - * and item references are not supported. + * The input to evaluate for every question. Provide a text string or an ordered array of + * messages, function calls and outputs, or custom tool calls and outputs. Each tool output must + * identify a matching call by call ID. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -66,7 +66,7 @@ private constructor( fun model(): String = body.model() /** - * Ask 1–200 questions about the input. Each question can be a predicate, choice, or score. + * Ask 1–400 questions about the input. Each question can be a predicate, choice, or score. * Answers come back in the same order. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -204,10 +204,9 @@ private constructor( fun body(body: Body) = apply { this.body = body.toBuilder() } /** - * The text or images to evaluate for every question. Provide a text string or user messages - * containing text and images. Images can be base64 data URLs or publicly accessible HTTP(S) - * URLs; at most 128 images are allowed across all messages in one request. Files, audio, - * tools, and item references are not supported. + * The input to evaluate for every question. Provide a text string or an ordered array of + * messages, function calls and outputs, or custom tool calls and outputs. Each tool output + * must identify a matching call by call ID. */ fun input(input: Input) = apply { body.input(input) } @@ -222,13 +221,10 @@ private constructor( /** Alias for calling [input] with `Input.ofString(string)`. */ fun input(string: String) = apply { body.input(string) } - /** - * Alias for calling [input] with `Input.ofDecisionInputMessages(decisionInputMessages)`. - */ - fun inputOfDecisionInputMessages(decisionInputMessages: List) = - apply { - body.inputOfDecisionInputMessages(decisionInputMessages) - } + /** Alias for calling [input] with `Input.ofDecisionInputItems(decisionInputItems)`. */ + fun inputOfDecisionInputItems(decisionInputItems: List) = apply { + body.inputOfDecisionInputItems(decisionInputItems) + } /** The model to use. Only `gpt-6-luna` is supported. */ fun model(model: String) = apply { body.model(model) } @@ -242,7 +238,7 @@ private constructor( fun model(model: JsonField) = apply { body.model(model) } /** - * Ask 1–200 questions about the input. Each question can be a predicate, choice, or score. + * Ask 1–400 questions about the input. Each question can be a predicate, choice, or score. * Answers come back in the same order. */ fun questions(questions: List) = apply { body.questions(questions) } @@ -274,10 +270,26 @@ private constructor( * .build() * ``` */ - fun addPredicateQuestion(instructions: String) = apply { + fun addPredicateQuestion(instructions: Question.Predicate.Instructions) = apply { body.addPredicateQuestion(instructions) } + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofString(string)`. + */ + fun addPredicateQuestion(string: String) = apply { body.addPredicateQuestion(string) } + + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun addPredicateQuestionOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = apply { + body.addPredicateQuestionOfDecisionQuestionInputParts(decisionQuestionInputParts) + } + /** Alias for calling [addQuestion] with `Question.ofChoice(choice)`. */ fun addQuestion(choice: Question.Choice) = apply { body.addQuestion(choice) } @@ -541,10 +553,9 @@ private constructor( ) /** - * The text or images to evaluate for every question. Provide a text string or user messages - * containing text and images. Images can be base64 data URLs or publicly accessible HTTP(S) - * URLs; at most 128 images are allowed across all messages in one request. Files, audio, - * tools, and item references are not supported. + * The input to evaluate for every question. Provide a text string or an ordered array of + * messages, function calls and outputs, or custom tool calls and outputs. Each tool output + * must identify a matching call by call ID. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -560,7 +571,7 @@ private constructor( fun model(): String = model.getRequired("model") /** - * Ask 1–200 questions about the input. Each question can be a predicate, choice, or score. + * Ask 1–400 questions about the input. Each question can be a predicate, choice, or score. * Answers come back in the same order. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -731,10 +742,9 @@ private constructor( } /** - * The text or images to evaluate for every question. Provide a text string or user - * messages containing text and images. Images can be base64 data URLs or publicly - * accessible HTTP(S) URLs; at most 128 images are allowed across all messages in one - * request. Files, audio, tools, and item references are not supported. + * The input to evaluate for every question. Provide a text string or an ordered array + * of messages, function calls and outputs, or custom tool calls and outputs. Each tool + * output must identify a matching call by call ID. */ fun input(input: Input) = input(JsonField.of(input)) @@ -750,12 +760,9 @@ private constructor( /** Alias for calling [input] with `Input.ofString(string)`. */ fun input(string: String) = input(Input.ofString(string)) - /** - * Alias for calling [input] with - * `Input.ofDecisionInputMessages(decisionInputMessages)`. - */ - fun inputOfDecisionInputMessages(decisionInputMessages: List) = - input(Input.ofDecisionInputMessages(decisionInputMessages)) + /** Alias for calling [input] with `Input.ofDecisionInputItems(decisionInputItems)`. */ + fun inputOfDecisionInputItems(decisionInputItems: List) = + input(Input.ofDecisionInputItems(decisionInputItems)) /** The model to use. Only `gpt-6-luna` is supported. */ fun model(model: String) = model(JsonField.of(model)) @@ -770,7 +777,7 @@ private constructor( fun model(model: JsonField) = apply { this.model = model } /** - * Ask 1–200 questions about the input. Each question can be a predicate, choice, or + * Ask 1–400 questions about the input. Each question can be a predicate, choice, or * score. Answers come back in the same order. */ fun questions(questions: List) = questions(JsonField.of(questions)) @@ -809,9 +816,29 @@ private constructor( * .build() * ``` */ - fun addPredicateQuestion(instructions: String) = + fun addPredicateQuestion(instructions: Question.Predicate.Instructions) = addQuestion(Question.Predicate.builder().instructions(instructions).build()) + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofString(string)`. + */ + fun addPredicateQuestion(string: String) = + addPredicateQuestion(Question.Predicate.Instructions.ofString(string)) + + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun addPredicateQuestionOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + addPredicateQuestion( + Question.Predicate.Instructions.ofDecisionQuestionInputParts( + decisionQuestionInputParts + ) + ) + /** Alias for calling [addQuestion] with `Question.ofChoice(choice)`. */ fun addQuestion(choice: Question.Choice) = addQuestion(Question.ofChoice(choice)) @@ -1020,33 +1047,32 @@ private constructor( } /** - * The text or images to evaluate for every question. Provide a text string or user messages - * containing text and images. Images can be base64 data URLs or publicly accessible HTTP(S) - * URLs; at most 128 images are allowed across all messages in one request. Files, audio, tools, - * and item references are not supported. + * The input to evaluate for every question. Provide a text string or an ordered array of + * messages, function calls and outputs, or custom tool calls and outputs. Each tool output must + * identify a matching call by call ID. */ @JsonDeserialize(using = Input.Deserializer::class) @JsonSerialize(using = Input.Serializer::class) class Input private constructor( private val string: String? = null, - private val decisionInputMessages: List? = null, + private val decisionInputItems: List? = null, private val _json: JsonValue? = null, ) { fun string(): Optional = Optional.ofNullable(string) - fun decisionInputMessages(): Optional> = - Optional.ofNullable(decisionInputMessages) + fun decisionInputItems(): Optional> = + Optional.ofNullable(decisionInputItems) fun isString(): Boolean = string != null - fun isDecisionInputMessages(): Boolean = decisionInputMessages != null + fun isDecisionInputItems(): Boolean = decisionInputItems != null fun asString(): String = string.getOrThrow("string") - fun asDecisionInputMessages(): List = - decisionInputMessages.getOrThrow("decisionInputMessages") + fun asDecisionInputItems(): List = + decisionInputItems.getOrThrow("decisionInputItems") fun _json(): Optional = Optional.ofNullable(_json) @@ -1082,8 +1108,7 @@ private constructor( fun accept(visitor: Visitor): T = when { string != null -> visitor.visitString(string) - decisionInputMessages != null -> - visitor.visitDecisionInputMessages(decisionInputMessages) + decisionInputItems != null -> visitor.visitDecisionInputItems(decisionInputItems) else -> visitor.unknown(_json) } @@ -1107,10 +1132,10 @@ private constructor( object : Visitor { override fun visitString(string: String) {} - override fun visitDecisionInputMessages( - decisionInputMessages: List + override fun visitDecisionInputItems( + decisionInputItems: List ) { - decisionInputMessages.forEach { it.validate() } + decisionInputItems.forEach { it.validate() } } } ) @@ -1137,9 +1162,9 @@ private constructor( object : Visitor { override fun visitString(string: String) = 1 - override fun visitDecisionInputMessages( - decisionInputMessages: List - ) = decisionInputMessages.sumOf { it.validity().toInt() } + override fun visitDecisionInputItems( + decisionInputItems: List + ) = decisionInputItems.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -1152,16 +1177,15 @@ private constructor( return other is Input && string == other.string && - decisionInputMessages == other.decisionInputMessages + decisionInputItems == other.decisionInputItems } - override fun hashCode(): Int = Objects.hash(string, decisionInputMessages) + override fun hashCode(): Int = Objects.hash(string, decisionInputItems) override fun toString(): String = when { string != null -> "Input{string=$string}" - decisionInputMessages != null -> - "Input{decisionInputMessages=$decisionInputMessages}" + decisionInputItems != null -> "Input{decisionInputItems=$decisionInputItems}" _json != null -> "Input{_unknown=$_json}" else -> throw IllegalStateException("Invalid Input") } @@ -1171,8 +1195,8 @@ private constructor( @JvmStatic fun ofString(string: String) = Input(string = string) @JvmStatic - fun ofDecisionInputMessages(decisionInputMessages: List) = - Input(decisionInputMessages = decisionInputMessages.toImmutable()) + fun ofDecisionInputItems(decisionInputItems: List) = + Input(decisionInputItems = decisionInputItems.toImmutable()) } /** An interface that defines how to map each variant of [Input] to a value of type [T]. */ @@ -1180,7 +1204,7 @@ private constructor( fun visitString(string: String): T - fun visitDecisionInputMessages(decisionInputMessages: List): T + fun visitDecisionInputItems(decisionInputItems: List): T /** * Maps an unknown variant of [Input] to a value of type [T]. @@ -1207,8 +1231,9 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { Input(string = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef>()) - ?.let { Input(decisionInputMessages = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Input(decisionInputItems = it, _json = json) + }, ) .filterNotNull() .allMaxBy { it.validity() } @@ -1235,8 +1260,8 @@ private constructor( ) { when { value.string != null -> generator.writeObject(value.string) - value.decisionInputMessages != null -> - generator.writeObject(value.decisionInputMessages) + value.decisionInputItems != null -> + generator.writeObject(value.decisionInputItems) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Input") } @@ -1492,7 +1517,7 @@ private constructor( class Predicate @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -1502,7 +1527,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(instructions, type, name, mutableMapOf()) @@ -1512,7 +1537,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `predicate`. @@ -1541,7 +1566,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -1603,7 +1628,7 @@ private constructor( /** A builder for [Predicate]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("predicate") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -1616,19 +1641,34 @@ private constructor( additionalProperties = predicate.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -1714,7 +1754,7 @@ private constructor( return@apply } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("predicate")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -1740,10 +1780,249 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("predicate")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1771,7 +2050,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val choices: JsonField>, - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -1784,7 +2063,7 @@ private constructor( choices: JsonField> = JsonMissing.of(), @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(choices, instructions, type, name, mutableMapOf()) @@ -1803,7 +2082,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `choice`. @@ -1841,7 +2120,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -1906,7 +2185,7 @@ private constructor( class Builder internal constructor() { private var choices: JsonField>? = null - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("choice") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -1946,19 +2225,34 @@ private constructor( } } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -2047,7 +2341,7 @@ private constructor( } choices().forEach { it.validate() } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("choice")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -2074,10 +2368,249 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("choice")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -2105,7 +2638,7 @@ private constructor( class Score @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val levels: JsonField>, private val type: JsonValue, private val name: JsonField, @@ -2116,7 +2649,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("levels") @ExcludeMissing levels: JsonField> = JsonMissing.of(), @@ -2129,7 +2662,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -2165,7 +2698,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [levels]. @@ -2236,7 +2769,7 @@ private constructor( /** A builder for [Score]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var levels: JsonField>? = null private var type: JsonValue = JsonValue.from("score") private var name: JsonField = JsonMissing.of() @@ -2251,19 +2784,34 @@ private constructor( additionalProperties = score.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + fun levels(levels: List) = levels(JsonField.of(levels)) /** @@ -2376,7 +2924,7 @@ private constructor( return@apply } - instructions() + instructions().validate() levels().forEach { it.validate() } _type().let { if (it != JsonValue.from("score")) { @@ -2403,11 +2951,250 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + (levels.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + type.let { if (it == JsonValue.from("score")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + class Level @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( diff --git a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputItem.kt new file mode 100644 index 000000000..14b8cad17 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputItem.kt @@ -0,0 +1,3412 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +package com.openai.models.decisions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.BaseDeserializer +import com.openai.core.BaseSerializer +import com.openai.core.Enum +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.collectAllProperties +import com.openai.core.getOrThrow +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** A user message containing input text, images, or JSON. */ +@JsonDeserialize(using = DecisionInputItem.Deserializer::class) +@JsonSerialize(using = DecisionInputItem.Serializer::class) +class DecisionInputItem +private constructor( + private val decisionInputMessage: DecisionInputMessage? = null, + private val functionCall: FunctionCall? = null, + private val functionCallOutput: FunctionCallOutput? = null, + private val customToolCall: CustomToolCall? = null, + private val customToolCallOutput: CustomToolCallOutput? = null, + private val _json: JsonValue? = null, +) { + + /** A user message containing input text, images, or JSON. */ + fun decisionInputMessage(): Optional = + Optional.ofNullable(decisionInputMessage) + + fun functionCall(): Optional = Optional.ofNullable(functionCall) + + fun functionCallOutput(): Optional = Optional.ofNullable(functionCallOutput) + + fun customToolCall(): Optional = Optional.ofNullable(customToolCall) + + fun customToolCallOutput(): Optional = + Optional.ofNullable(customToolCallOutput) + + fun isDecisionInputMessage(): Boolean = decisionInputMessage != null + + fun isFunctionCall(): Boolean = functionCall != null + + fun isFunctionCallOutput(): Boolean = functionCallOutput != null + + fun isCustomToolCall(): Boolean = customToolCall != null + + fun isCustomToolCallOutput(): Boolean = customToolCallOutput != null + + /** A user message containing input text, images, or JSON. */ + fun asDecisionInputMessage(): DecisionInputMessage = + decisionInputMessage.getOrThrow("decisionInputMessage") + + fun asFunctionCall(): FunctionCall = functionCall.getOrThrow("functionCall") + + fun asFunctionCallOutput(): FunctionCallOutput = + functionCallOutput.getOrThrow("functionCallOutput") + + fun asCustomToolCall(): CustomToolCall = customToolCall.getOrThrow("customToolCall") + + fun asCustomToolCallOutput(): CustomToolCallOutput = + customToolCallOutput.getOrThrow("customToolCallOutput") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = decisionInputItem.accept(new DecisionInputItem.Visitor>() { + * @Override + * public Optional visitDecisionInputMessage(DecisionInputMessage decisionInputMessage) { + * return Optional.of(decisionInputMessage.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and + * the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + decisionInputMessage != null -> visitor.visitDecisionInputMessage(decisionInputMessage) + functionCall != null -> visitor.visitFunctionCall(functionCall) + functionCallOutput != null -> visitor.visitFunctionCallOutput(functionCallOutput) + customToolCall != null -> visitor.visitCustomToolCall(customToolCall) + customToolCallOutput != null -> visitor.visitCustomToolCallOutput(customToolCallOutput) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): DecisionInputItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitDecisionInputMessage(decisionInputMessage: DecisionInputMessage) { + decisionInputMessage.validate() + } + + override fun visitFunctionCall(functionCall: FunctionCall) { + functionCall.validate() + } + + override fun visitFunctionCallOutput(functionCallOutput: FunctionCallOutput) { + functionCallOutput.validate() + } + + override fun visitCustomToolCall(customToolCall: CustomToolCall) { + customToolCall.validate() + } + + override fun visitCustomToolCallOutput(customToolCallOutput: CustomToolCallOutput) { + customToolCallOutput.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 visitDecisionInputMessage(decisionInputMessage: DecisionInputMessage) = + decisionInputMessage.validity() + + override fun visitFunctionCall(functionCall: FunctionCall) = functionCall.validity() + + override fun visitFunctionCallOutput(functionCallOutput: FunctionCallOutput) = + functionCallOutput.validity() + + override fun visitCustomToolCall(customToolCall: CustomToolCall) = + customToolCall.validity() + + override fun visitCustomToolCallOutput(customToolCallOutput: CustomToolCallOutput) = + customToolCallOutput.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DecisionInputItem && + decisionInputMessage == other.decisionInputMessage && + functionCall == other.functionCall && + functionCallOutput == other.functionCallOutput && + customToolCall == other.customToolCall && + customToolCallOutput == other.customToolCallOutput + } + + override fun hashCode(): Int = + Objects.hash( + decisionInputMessage, + functionCall, + functionCallOutput, + customToolCall, + customToolCallOutput, + ) + + override fun toString(): String = + when { + decisionInputMessage != null -> + "DecisionInputItem{decisionInputMessage=$decisionInputMessage}" + functionCall != null -> "DecisionInputItem{functionCall=$functionCall}" + functionCallOutput != null -> + "DecisionInputItem{functionCallOutput=$functionCallOutput}" + customToolCall != null -> "DecisionInputItem{customToolCall=$customToolCall}" + customToolCallOutput != null -> + "DecisionInputItem{customToolCallOutput=$customToolCallOutput}" + _json != null -> "DecisionInputItem{_unknown=$_json}" + else -> throw IllegalStateException("Invalid DecisionInputItem") + } + + companion object { + + /** A user message containing input text, images, or JSON. */ + @JvmStatic + fun ofDecisionInputMessage(decisionInputMessage: DecisionInputMessage) = + DecisionInputItem(decisionInputMessage = decisionInputMessage) + + @JvmStatic + fun ofFunctionCall(functionCall: FunctionCall) = + DecisionInputItem(functionCall = functionCall) + + @JvmStatic + fun ofFunctionCallOutput(functionCallOutput: FunctionCallOutput) = + DecisionInputItem(functionCallOutput = functionCallOutput) + + @JvmStatic + fun ofCustomToolCall(customToolCall: CustomToolCall) = + DecisionInputItem(customToolCall = customToolCall) + + @JvmStatic + fun ofCustomToolCallOutput(customToolCallOutput: CustomToolCallOutput) = + DecisionInputItem(customToolCallOutput = customToolCallOutput) + } + + /** + * An interface that defines how to map each variant of [DecisionInputItem] to a value of type + * [T]. + */ + interface Visitor { + + /** A user message containing input text, images, or JSON. */ + fun visitDecisionInputMessage(decisionInputMessage: DecisionInputMessage): T + + fun visitFunctionCall(functionCall: FunctionCall): T + + fun visitFunctionCallOutput(functionCallOutput: FunctionCallOutput): T + + fun visitCustomToolCall(customToolCall: CustomToolCall): T + + fun visitCustomToolCallOutput(customToolCallOutput: CustomToolCallOutput): T + + /** + * Maps an unknown variant of [DecisionInputItem] to a value of type [T]. + * + * An instance of [DecisionInputItem] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is unaware + * of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown DecisionInputItem") + } + } + + internal class Deserializer : BaseDeserializer(DecisionInputItem::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): DecisionInputItem { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "function_call" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputItem(functionCall = it, _json = json) + } ?: DecisionInputItem(_json = json) + } + "function_call_output" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputItem(functionCallOutput = it, _json = json) + } ?: DecisionInputItem(_json = json) + } + "custom_tool_call" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputItem(customToolCall = it, _json = json) + } ?: DecisionInputItem(_json = json) + } + "custom_tool_call_output" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputItem(customToolCallOutput = it, _json = json) + } ?: DecisionInputItem(_json = json) + } + } + + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputItem(decisionInputMessage = it, _json = json) + } ?: DecisionInputItem(_json = json) + } + } + + internal class Serializer : BaseSerializer(DecisionInputItem::class) { + + override fun serialize( + value: DecisionInputItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.decisionInputMessage != null -> + generator.writeObject(value.decisionInputMessage) + value.functionCall != null -> generator.writeObject(value.functionCall) + value.functionCallOutput != null -> generator.writeObject(value.functionCallOutput) + value.customToolCall != null -> generator.writeObject(value.customToolCall) + value.customToolCallOutput != null -> + generator.writeObject(value.customToolCallOutput) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid DecisionInputItem") + } + } + } + + class FunctionCall + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val arguments: JsonField, + private val callId: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val id: JsonField, + private val namespace: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("arguments") + @ExcludeMissing + arguments: JsonField = JsonMissing.of(), + @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("namespace") + @ExcludeMissing + namespace: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(arguments, callId, name, type, id, namespace, status, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun arguments(): String = arguments.getRequired("arguments") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun callId(): String = callId.getRequired("call_id") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("function_call") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun namespace(): Optional = namespace.getOptional("namespace") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun status(): Optional = status.getOptional("status") + + /** + * Returns the raw JSON value of [arguments]. + * + * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("arguments") @ExcludeMissing fun _arguments(): JsonField = arguments + + /** + * Returns the raw JSON value of [callId]. + * + * Unlike [callId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("call_id") @ExcludeMissing fun _callId(): JsonField = callId + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [namespace]. + * + * Unlike [namespace], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("namespace") @ExcludeMissing fun _namespace(): JsonField = namespace + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "arguments" to this.arguments, + "call_id" to this.callId, + "name" to this.name, + "type" to this.type, + "id" to this.id, + "namespace" to this.namespace, + "status" to this.status, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [FunctionCall]. + * + * The following fields are required: + * ```java + * .arguments() + * .callId() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FunctionCall]. */ + class Builder internal constructor() { + + private var arguments: JsonField? = null + private var callId: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("function_call") + private var id: JsonField = JsonMissing.of() + private var namespace: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(functionCall: FunctionCall) = apply { + arguments = functionCall.arguments + callId = functionCall.callId + name = functionCall.name + type = functionCall.type + id = functionCall.id + namespace = functionCall.namespace + status = functionCall.status + additionalProperties = functionCall.additionalProperties.toMutableMap() + } + + fun arguments(arguments: String) = arguments(JsonField.of(arguments)) + + /** + * Sets [Builder.arguments] to an arbitrary JSON value. + * + * You should usually call [Builder.arguments] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun arguments(arguments: JsonField) = apply { this.arguments = arguments } + + fun callId(callId: String) = callId(JsonField.of(callId)) + + /** + * Sets [Builder.callId] to an arbitrary JSON value. + * + * You should usually call [Builder.callId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun callId(callId: JsonField) = apply { this.callId = callId } + + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("function_call") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun id(id: String?) = id(JsonField.ofNullable(id)) + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun namespace(namespace: String?) = namespace(JsonField.ofNullable(namespace)) + + /** Alias for calling [Builder.namespace] with `namespace.orElse(null)`. */ + fun namespace(namespace: Optional) = namespace(namespace.getOrNull()) + + /** + * Sets [Builder.namespace] to an arbitrary JSON value. + * + * You should usually call [Builder.namespace] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun namespace(namespace: JsonField) = apply { this.namespace = namespace } + + fun status(status: Status?) = status(JsonField.ofNullable(status)) + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [FunctionCall]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .arguments() + * .callId() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): FunctionCall = + FunctionCall( + checkRequired("arguments", arguments), + checkRequired("callId", callId), + checkRequired("name", name), + type, + id, + namespace, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): FunctionCall = apply { + if (validated) { + return@apply + } + + arguments() + callId() + name() + _type().let { + if (it != JsonValue.from("function_call")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + id() + namespace() + 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) + + (if (namespace.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val IN_PROGRESS = of("in_progress") + + @JvmField val COMPLETED = of("completed") + + @JvmField val INCOMPLETE = of("incomplete") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + /** + * An enum member indicating that [Status] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IN_PROGRESS -> Value.IN_PROGRESS + COMPLETED -> Value.COMPLETED + INCOMPLETE -> Value.INCOMPLETE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + IN_PROGRESS -> Known.IN_PROGRESS + COMPLETED -> Known.COMPLETED + INCOMPLETE -> Known.INCOMPLETE + else -> throw OpenAIInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + 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 + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FunctionCall && + arguments == other.arguments && + callId == other.callId && + name == other.name && + type == other.type && + id == other.id && + namespace == other.namespace && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(arguments, callId, name, type, id, namespace, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "FunctionCall{arguments=$arguments, callId=$callId, name=$name, type=$type, id=$id, namespace=$namespace, status=$status, additionalProperties=$additionalProperties}" + } + + class FunctionCallOutput + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val callId: JsonField, + private val output: JsonField, + private val type: JsonValue, + private val id: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), + @JsonProperty("output") @ExcludeMissing output: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(callId, output, type, id, status, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun callId(): String = callId.getRequired("call_id") + + /** + * Text evidence or an ordered list of text and image parts. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun output(): Output = output.getRequired("output") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("function_call_output") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun status(): Optional = status.getOptional("status") + + /** + * Returns the raw JSON value of [callId]. + * + * Unlike [callId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("call_id") @ExcludeMissing fun _callId(): JsonField = callId + + /** + * Returns the raw JSON value of [output]. + * + * Unlike [output], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "call_id" to this.callId, + "output" to this.output, + "type" to this.type, + "id" to this.id, + "status" to this.status, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [FunctionCallOutput]. + * + * The following fields are required: + * ```java + * .callId() + * .output() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FunctionCallOutput]. */ + class Builder internal constructor() { + + private var callId: JsonField? = null + private var output: JsonField? = null + private var type: JsonValue = JsonValue.from("function_call_output") + private var id: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(functionCallOutput: FunctionCallOutput) = apply { + callId = functionCallOutput.callId + output = functionCallOutput.output + type = functionCallOutput.type + id = functionCallOutput.id + status = functionCallOutput.status + additionalProperties = functionCallOutput.additionalProperties.toMutableMap() + } + + fun callId(callId: String) = callId(JsonField.of(callId)) + + /** + * Sets [Builder.callId] to an arbitrary JSON value. + * + * You should usually call [Builder.callId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun callId(callId: JsonField) = apply { this.callId = callId } + + /** Text evidence or an ordered list of text and image parts. */ + fun output(output: Output) = output(JsonField.of(output)) + + /** + * Sets [Builder.output] to an arbitrary JSON value. + * + * You should usually call [Builder.output] with a well-typed [Output] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun output(output: JsonField) = apply { this.output = output } + + /** Alias for calling [output] with `Output.ofString(string)`. */ + fun output(string: String) = output(Output.ofString(string)) + + /** + * Alias for calling [output] with + * `Output.ofUnnamedSchemaWithArrayParent1s(unnamedSchemaWithArrayParent1s)`. + */ + fun outputOfUnnamedSchemaWithArrayParent1s( + unnamedSchemaWithArrayParent1s: List + ) = output(Output.ofUnnamedSchemaWithArrayParent1s(unnamedSchemaWithArrayParent1s)) + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("function_call_output") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun id(id: String?) = id(JsonField.ofNullable(id)) + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun status(status: Status?) = status(JsonField.ofNullable(status)) + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [FunctionCallOutput]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .callId() + * .output() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): FunctionCallOutput = + FunctionCallOutput( + checkRequired("callId", callId), + checkRequired("output", output), + type, + id, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): FunctionCallOutput = apply { + if (validated) { + return@apply + } + + callId() + output().validate() + _type().let { + if (it != JsonValue.from("function_call_output")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + id() + 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("function_call_output")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + /** Text evidence or an ordered list of text and image parts. */ + @JsonDeserialize(using = Output.Deserializer::class) + @JsonSerialize(using = Output.Serializer::class) + class Output + private constructor( + private val string: String? = null, + private val unnamedSchemaWithArrayParent1s: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun unnamedSchemaWithArrayParent1s(): Optional> = + Optional.ofNullable(unnamedSchemaWithArrayParent1s) + + fun isString(): Boolean = string != null + + fun isUnnamedSchemaWithArrayParent1s(): Boolean = unnamedSchemaWithArrayParent1s != null + + fun asString(): String = string.getOrThrow("string") + + fun asUnnamedSchemaWithArrayParent1s(): List = + unnamedSchemaWithArrayParent1s.getOrThrow("unnamedSchemaWithArrayParent1s") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = output.accept(new Output.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + unnamedSchemaWithArrayParent1s != null -> + visitor.visitUnnamedSchemaWithArrayParent1s(unnamedSchemaWithArrayParent1s) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Output = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitUnnamedSchemaWithArrayParent1s( + unnamedSchemaWithArrayParent1s: List + ) { + unnamedSchemaWithArrayParent1s.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitUnnamedSchemaWithArrayParent1s( + unnamedSchemaWithArrayParent1s: List + ) = unnamedSchemaWithArrayParent1s.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Output && + string == other.string && + unnamedSchemaWithArrayParent1s == other.unnamedSchemaWithArrayParent1s + } + + override fun hashCode(): Int = Objects.hash(string, unnamedSchemaWithArrayParent1s) + + override fun toString(): String = + when { + string != null -> "Output{string=$string}" + unnamedSchemaWithArrayParent1s != null -> + "Output{unnamedSchemaWithArrayParent1s=$unnamedSchemaWithArrayParent1s}" + _json != null -> "Output{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Output") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Output(string = string) + + @JvmStatic + fun ofUnnamedSchemaWithArrayParent1s( + unnamedSchemaWithArrayParent1s: List + ) = + Output( + unnamedSchemaWithArrayParent1s = + unnamedSchemaWithArrayParent1s.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Output] to a value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitUnnamedSchemaWithArrayParent1s( + unnamedSchemaWithArrayParent1s: List + ): T + + /** + * Maps an unknown variant of [Output] to a value of type [T]. + * + * An instance of [Output] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Output") + } + } + + internal class Deserializer : BaseDeserializer(Output::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Output { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Output(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Output(unnamedSchemaWithArrayParent1s = 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 -> Output(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the + // first completely valid match, or simply the first match if none are + // completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Output::class) { + + override fun serialize( + value: Output, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.unnamedSchemaWithArrayParent1s != null -> + generator.writeObject(value.unnamedSchemaWithArrayParent1s) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Output") + } + } + } + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs + * are not supported. + */ + @JsonDeserialize(using = UnnamedSchemaWithArrayParent1.Deserializer::class) + @JsonSerialize(using = UnnamedSchemaWithArrayParent1.Serializer::class) + class UnnamedSchemaWithArrayParent1 + private constructor( + private val inputText: DecisionInputText? = null, + private val inputImage: DecisionInputImage? = null, + private val _json: JsonValue? = null, + ) { + + fun inputText(): Optional = Optional.ofNullable(inputText) + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File + * IDs are not supported. + */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + fun isInputText(): Boolean = inputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun asInputText(): DecisionInputText = inputText.getOrThrow("inputText") + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File + * IDs are not supported. + */ + fun asInputImage(): DecisionInputImage = inputImage.getOrThrow("inputImage") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = unnamedSchemaWithArrayParent1.accept(new UnnamedSchemaWithArrayParent1.Visitor>() { + * @Override + * public Optional visitInputText(DecisionInputText inputText) { + * return Optional.of(inputText.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + inputText != null -> visitor.visitInputText(inputText) + inputImage != null -> visitor.visitInputImage(inputImage) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): UnnamedSchemaWithArrayParent1 = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitInputText(inputText: DecisionInputText) { + inputText.validate() + } + + override fun visitInputImage(inputImage: DecisionInputImage) { + inputImage.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 visitInputText(inputText: DecisionInputText) = + inputText.validity() + + override fun visitInputImage(inputImage: DecisionInputImage) = + inputImage.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UnnamedSchemaWithArrayParent1 && + inputText == other.inputText && + inputImage == other.inputImage + } + + override fun hashCode(): Int = Objects.hash(inputText, inputImage) + + override fun toString(): String = + when { + inputText != null -> "UnnamedSchemaWithArrayParent1{inputText=$inputText}" + inputImage != null -> + "UnnamedSchemaWithArrayParent1{inputImage=$inputImage}" + _json != null -> "UnnamedSchemaWithArrayParent1{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent1") + } + + companion object { + + @JvmStatic + fun ofInputText(inputText: DecisionInputText) = + UnnamedSchemaWithArrayParent1(inputText = inputText) + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. + * File IDs are not supported. + */ + @JvmStatic + fun ofInputImage(inputImage: DecisionInputImage) = + UnnamedSchemaWithArrayParent1(inputImage = inputImage) + } + + /** + * An interface that defines how to map each variant of + * [UnnamedSchemaWithArrayParent1] to a value of type [T]. + */ + interface Visitor { + + fun visitInputText(inputText: DecisionInputText): T + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. + * File IDs are not supported. + */ + fun visitInputImage(inputImage: DecisionInputImage): T + + /** + * Maps an unknown variant of [UnnamedSchemaWithArrayParent1] to a value of type + * [T]. + * + * An instance of [UnnamedSchemaWithArrayParent1] can contain an unknown variant + * if it was deserialized from data that doesn't match any known variant. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown UnnamedSchemaWithArrayParent1") + } + } + + internal class Deserializer : + BaseDeserializer( + UnnamedSchemaWithArrayParent1::class + ) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): UnnamedSchemaWithArrayParent1 { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "input_text" -> { + return tryDeserialize(node, jacksonTypeRef()) + ?.let { + UnnamedSchemaWithArrayParent1(inputText = it, _json = json) + } ?: UnnamedSchemaWithArrayParent1(_json = json) + } + "input_image" -> { + return tryDeserialize(node, jacksonTypeRef()) + ?.let { + UnnamedSchemaWithArrayParent1(inputImage = it, _json = json) + } ?: UnnamedSchemaWithArrayParent1(_json = json) + } + } + + return UnnamedSchemaWithArrayParent1(_json = json) + } + } + + internal class Serializer : + BaseSerializer( + UnnamedSchemaWithArrayParent1::class + ) { + + override fun serialize( + value: UnnamedSchemaWithArrayParent1, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.inputText != null -> generator.writeObject(value.inputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent1") + } + } + } + } + } + + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val IN_PROGRESS = of("in_progress") + + @JvmField val COMPLETED = of("completed") + + @JvmField val INCOMPLETE = of("incomplete") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + /** + * An enum member indicating that [Status] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IN_PROGRESS -> Value.IN_PROGRESS + COMPLETED -> Value.COMPLETED + INCOMPLETE -> Value.INCOMPLETE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + IN_PROGRESS -> Known.IN_PROGRESS + COMPLETED -> Known.COMPLETED + INCOMPLETE -> Known.INCOMPLETE + else -> throw OpenAIInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + 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 + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FunctionCallOutput && + callId == other.callId && + output == other.output && + type == other.type && + id == other.id && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(callId, output, type, id, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "FunctionCallOutput{callId=$callId, output=$output, type=$type, id=$id, status=$status, additionalProperties=$additionalProperties}" + } + + class CustomToolCall + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val callId: JsonField, + private val input: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val id: JsonField, + private val namespace: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), + @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("namespace") + @ExcludeMissing + namespace: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(callId, input, name, type, id, namespace, status, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun callId(): String = callId.getRequired("call_id") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun input(): String = input.getRequired("input") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("custom_tool_call") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun namespace(): Optional = namespace.getOptional("namespace") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun status(): Optional = status.getOptional("status") + + /** + * Returns the raw JSON value of [callId]. + * + * Unlike [callId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("call_id") @ExcludeMissing fun _callId(): JsonField = callId + + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [namespace]. + * + * Unlike [namespace], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("namespace") @ExcludeMissing fun _namespace(): JsonField = namespace + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "call_id" to this.callId, + "input" to this.input, + "name" to this.name, + "type" to this.type, + "id" to this.id, + "namespace" to this.namespace, + "status" to this.status, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomToolCall]. + * + * The following fields are required: + * ```java + * .callId() + * .input() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CustomToolCall]. */ + class Builder internal constructor() { + + private var callId: JsonField? = null + private var input: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("custom_tool_call") + private var id: JsonField = JsonMissing.of() + private var namespace: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(customToolCall: CustomToolCall) = apply { + callId = customToolCall.callId + input = customToolCall.input + name = customToolCall.name + type = customToolCall.type + id = customToolCall.id + namespace = customToolCall.namespace + status = customToolCall.status + additionalProperties = customToolCall.additionalProperties.toMutableMap() + } + + fun callId(callId: String) = callId(JsonField.of(callId)) + + /** + * Sets [Builder.callId] to an arbitrary JSON value. + * + * You should usually call [Builder.callId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun callId(callId: JsonField) = apply { this.callId = callId } + + fun input(input: String) = input(JsonField.of(input)) + + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun input(input: JsonField) = apply { this.input = input } + + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("custom_tool_call") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun id(id: String?) = id(JsonField.ofNullable(id)) + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun namespace(namespace: String?) = namespace(JsonField.ofNullable(namespace)) + + /** Alias for calling [Builder.namespace] with `namespace.orElse(null)`. */ + fun namespace(namespace: Optional) = namespace(namespace.getOrNull()) + + /** + * Sets [Builder.namespace] to an arbitrary JSON value. + * + * You should usually call [Builder.namespace] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun namespace(namespace: JsonField) = apply { this.namespace = namespace } + + fun status(status: Status?) = status(JsonField.ofNullable(status)) + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomToolCall]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .callId() + * .input() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomToolCall = + CustomToolCall( + checkRequired("callId", callId), + checkRequired("input", input), + checkRequired("name", name), + type, + id, + namespace, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): CustomToolCall = apply { + if (validated) { + return@apply + } + + callId() + input() + name() + _type().let { + if (it != JsonValue.from("custom_tool_call")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + id() + namespace() + 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 (input.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("custom_tool_call")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (if (namespace.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val IN_PROGRESS = of("in_progress") + + @JvmField val COMPLETED = of("completed") + + @JvmField val INCOMPLETE = of("incomplete") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + /** + * An enum member indicating that [Status] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IN_PROGRESS -> Value.IN_PROGRESS + COMPLETED -> Value.COMPLETED + INCOMPLETE -> Value.INCOMPLETE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + IN_PROGRESS -> Known.IN_PROGRESS + COMPLETED -> Known.COMPLETED + INCOMPLETE -> Known.INCOMPLETE + else -> throw OpenAIInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + 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 + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomToolCall && + callId == other.callId && + input == other.input && + name == other.name && + type == other.type && + id == other.id && + namespace == other.namespace && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(callId, input, name, type, id, namespace, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomToolCall{callId=$callId, input=$input, name=$name, type=$type, id=$id, namespace=$namespace, status=$status, additionalProperties=$additionalProperties}" + } + + class CustomToolCallOutput + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val callId: JsonField, + private val output: JsonField, + private val type: JsonValue, + private val id: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), + @JsonProperty("output") @ExcludeMissing output: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(callId, output, type, id, status, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun callId(): String = callId.getRequired("call_id") + + /** + * Text evidence or an ordered list of text and image parts. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun output(): Output = output.getRequired("output") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("custom_tool_call_output") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun status(): Optional = status.getOptional("status") + + /** + * Returns the raw JSON value of [callId]. + * + * Unlike [callId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("call_id") @ExcludeMissing fun _callId(): JsonField = callId + + /** + * Returns the raw JSON value of [output]. + * + * Unlike [output], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "call_id" to this.callId, + "output" to this.output, + "type" to this.type, + "id" to this.id, + "status" to this.status, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomToolCallOutput]. + * + * The following fields are required: + * ```java + * .callId() + * .output() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CustomToolCallOutput]. */ + class Builder internal constructor() { + + private var callId: JsonField? = null + private var output: JsonField? = null + private var type: JsonValue = JsonValue.from("custom_tool_call_output") + private var id: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(customToolCallOutput: CustomToolCallOutput) = apply { + callId = customToolCallOutput.callId + output = customToolCallOutput.output + type = customToolCallOutput.type + id = customToolCallOutput.id + status = customToolCallOutput.status + additionalProperties = customToolCallOutput.additionalProperties.toMutableMap() + } + + fun callId(callId: String) = callId(JsonField.of(callId)) + + /** + * Sets [Builder.callId] to an arbitrary JSON value. + * + * You should usually call [Builder.callId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun callId(callId: JsonField) = apply { this.callId = callId } + + /** Text evidence or an ordered list of text and image parts. */ + fun output(output: Output) = output(JsonField.of(output)) + + /** + * Sets [Builder.output] to an arbitrary JSON value. + * + * You should usually call [Builder.output] with a well-typed [Output] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun output(output: JsonField) = apply { this.output = output } + + /** Alias for calling [output] with `Output.ofString(string)`. */ + fun output(string: String) = output(Output.ofString(string)) + + /** + * Alias for calling [output] with + * `Output.ofUnnamedSchemaWithArrayParent2s(unnamedSchemaWithArrayParent2s)`. + */ + fun outputOfUnnamedSchemaWithArrayParent2s( + unnamedSchemaWithArrayParent2s: List + ) = output(Output.ofUnnamedSchemaWithArrayParent2s(unnamedSchemaWithArrayParent2s)) + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("custom_tool_call_output") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun id(id: String?) = id(JsonField.ofNullable(id)) + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun status(status: Status?) = status(JsonField.ofNullable(status)) + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomToolCallOutput]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .callId() + * .output() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomToolCallOutput = + CustomToolCallOutput( + checkRequired("callId", callId), + checkRequired("output", output), + type, + id, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): CustomToolCallOutput = apply { + if (validated) { + return@apply + } + + callId() + output().validate() + _type().let { + if (it != JsonValue.from("custom_tool_call_output")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + id() + 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("custom_tool_call_output")) 1 else 0 } + + (if (id.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + /** Text evidence or an ordered list of text and image parts. */ + @JsonDeserialize(using = Output.Deserializer::class) + @JsonSerialize(using = Output.Serializer::class) + class Output + private constructor( + private val string: String? = null, + private val unnamedSchemaWithArrayParent2s: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun unnamedSchemaWithArrayParent2s(): Optional> = + Optional.ofNullable(unnamedSchemaWithArrayParent2s) + + fun isString(): Boolean = string != null + + fun isUnnamedSchemaWithArrayParent2s(): Boolean = unnamedSchemaWithArrayParent2s != null + + fun asString(): String = string.getOrThrow("string") + + fun asUnnamedSchemaWithArrayParent2s(): List = + unnamedSchemaWithArrayParent2s.getOrThrow("unnamedSchemaWithArrayParent2s") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = output.accept(new Output.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + unnamedSchemaWithArrayParent2s != null -> + visitor.visitUnnamedSchemaWithArrayParent2s(unnamedSchemaWithArrayParent2s) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Output = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitUnnamedSchemaWithArrayParent2s( + unnamedSchemaWithArrayParent2s: List + ) { + unnamedSchemaWithArrayParent2s.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitUnnamedSchemaWithArrayParent2s( + unnamedSchemaWithArrayParent2s: List + ) = unnamedSchemaWithArrayParent2s.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Output && + string == other.string && + unnamedSchemaWithArrayParent2s == other.unnamedSchemaWithArrayParent2s + } + + override fun hashCode(): Int = Objects.hash(string, unnamedSchemaWithArrayParent2s) + + override fun toString(): String = + when { + string != null -> "Output{string=$string}" + unnamedSchemaWithArrayParent2s != null -> + "Output{unnamedSchemaWithArrayParent2s=$unnamedSchemaWithArrayParent2s}" + _json != null -> "Output{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Output") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Output(string = string) + + @JvmStatic + fun ofUnnamedSchemaWithArrayParent2s( + unnamedSchemaWithArrayParent2s: List + ) = + Output( + unnamedSchemaWithArrayParent2s = + unnamedSchemaWithArrayParent2s.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Output] to a value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitUnnamedSchemaWithArrayParent2s( + unnamedSchemaWithArrayParent2s: List + ): T + + /** + * Maps an unknown variant of [Output] to a value of type [T]. + * + * An instance of [Output] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Output") + } + } + + internal class Deserializer : BaseDeserializer(Output::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Output { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Output(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Output(unnamedSchemaWithArrayParent2s = 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 -> Output(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the + // first completely valid match, or simply the first match if none are + // completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Output::class) { + + override fun serialize( + value: Output, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.unnamedSchemaWithArrayParent2s != null -> + generator.writeObject(value.unnamedSchemaWithArrayParent2s) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Output") + } + } + } + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs + * are not supported. + */ + @JsonDeserialize(using = UnnamedSchemaWithArrayParent2.Deserializer::class) + @JsonSerialize(using = UnnamedSchemaWithArrayParent2.Serializer::class) + class UnnamedSchemaWithArrayParent2 + private constructor( + private val inputText: DecisionInputText? = null, + private val inputImage: DecisionInputImage? = null, + private val _json: JsonValue? = null, + ) { + + fun inputText(): Optional = Optional.ofNullable(inputText) + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File + * IDs are not supported. + */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + fun isInputText(): Boolean = inputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun asInputText(): DecisionInputText = inputText.getOrThrow("inputText") + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File + * IDs are not supported. + */ + fun asInputImage(): DecisionInputImage = inputImage.getOrThrow("inputImage") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = unnamedSchemaWithArrayParent2.accept(new UnnamedSchemaWithArrayParent2.Visitor>() { + * @Override + * public Optional visitInputText(DecisionInputText inputText) { + * return Optional.of(inputText.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + inputText != null -> visitor.visitInputText(inputText) + inputImage != null -> visitor.visitInputImage(inputImage) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): UnnamedSchemaWithArrayParent2 = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitInputText(inputText: DecisionInputText) { + inputText.validate() + } + + override fun visitInputImage(inputImage: DecisionInputImage) { + inputImage.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 visitInputText(inputText: DecisionInputText) = + inputText.validity() + + override fun visitInputImage(inputImage: DecisionInputImage) = + inputImage.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UnnamedSchemaWithArrayParent2 && + inputText == other.inputText && + inputImage == other.inputImage + } + + override fun hashCode(): Int = Objects.hash(inputText, inputImage) + + override fun toString(): String = + when { + inputText != null -> "UnnamedSchemaWithArrayParent2{inputText=$inputText}" + inputImage != null -> + "UnnamedSchemaWithArrayParent2{inputImage=$inputImage}" + _json != null -> "UnnamedSchemaWithArrayParent2{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent2") + } + + companion object { + + @JvmStatic + fun ofInputText(inputText: DecisionInputText) = + UnnamedSchemaWithArrayParent2(inputText = inputText) + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. + * File IDs are not supported. + */ + @JvmStatic + fun ofInputImage(inputImage: DecisionInputImage) = + UnnamedSchemaWithArrayParent2(inputImage = inputImage) + } + + /** + * An interface that defines how to map each variant of + * [UnnamedSchemaWithArrayParent2] to a value of type [T]. + */ + interface Visitor { + + fun visitInputText(inputText: DecisionInputText): T + + /** + * An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. + * File IDs are not supported. + */ + fun visitInputImage(inputImage: DecisionInputImage): T + + /** + * Maps an unknown variant of [UnnamedSchemaWithArrayParent2] to a value of type + * [T]. + * + * An instance of [UnnamedSchemaWithArrayParent2] can contain an unknown variant + * if it was deserialized from data that doesn't match any known variant. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown UnnamedSchemaWithArrayParent2") + } + } + + internal class Deserializer : + BaseDeserializer( + UnnamedSchemaWithArrayParent2::class + ) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): UnnamedSchemaWithArrayParent2 { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "input_text" -> { + return tryDeserialize(node, jacksonTypeRef()) + ?.let { + UnnamedSchemaWithArrayParent2(inputText = it, _json = json) + } ?: UnnamedSchemaWithArrayParent2(_json = json) + } + "input_image" -> { + return tryDeserialize(node, jacksonTypeRef()) + ?.let { + UnnamedSchemaWithArrayParent2(inputImage = it, _json = json) + } ?: UnnamedSchemaWithArrayParent2(_json = json) + } + } + + return UnnamedSchemaWithArrayParent2(_json = json) + } + } + + internal class Serializer : + BaseSerializer( + UnnamedSchemaWithArrayParent2::class + ) { + + override fun serialize( + value: UnnamedSchemaWithArrayParent2, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.inputText != null -> generator.writeObject(value.inputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent2") + } + } + } + } + } + + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val IN_PROGRESS = of("in_progress") + + @JvmField val COMPLETED = of("completed") + + @JvmField val INCOMPLETE = of("incomplete") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IN_PROGRESS, + COMPLETED, + INCOMPLETE, + /** + * An enum member indicating that [Status] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IN_PROGRESS -> Value.IN_PROGRESS + COMPLETED -> Value.COMPLETED + INCOMPLETE -> Value.INCOMPLETE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + IN_PROGRESS -> Known.IN_PROGRESS + COMPLETED -> Known.COMPLETED + INCOMPLETE -> Known.INCOMPLETE + else -> throw OpenAIInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + 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 + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomToolCallOutput && + callId == other.callId && + output == other.output && + type == other.type && + id == other.id && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(callId, output, type, id, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomToolCallOutput{callId=$callId, output=$output, type=$type, id=$id, status=$status, additionalProperties=$additionalProperties}" + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputMessage.kt index e6469ce49..c87adf85e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputMessage.kt @@ -31,311 +31,485 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** A user message containing text or images. */ +/** A user message containing input text, images, or JSON. */ +@JsonDeserialize(using = DecisionInputMessage.Deserializer::class) +@JsonSerialize(using = DecisionInputMessage.Serializer::class) class DecisionInputMessage -@JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val content: JsonField, - private val role: JsonValue, - private val type: JsonField, - private val additionalProperties: MutableMap, + private val user: User? = null, + private val developer: Developer? = null, + private val system: System? = null, + private val assistant: Assistant? = null, + private val _json: JsonValue? = null, ) { - @JsonCreator - private constructor( - @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), - @JsonProperty("role") @ExcludeMissing role: JsonValue = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) + /** A user message containing input text, images, or JSON. */ + fun user(): Optional = Optional.ofNullable(user) - /** - * Text evidence or an ordered list of text and image parts. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected value). - */ - fun content(): Content = content.getRequired("content") + /** A developer message containing input text. */ + fun developer(): Optional = Optional.ofNullable(developer) + + /** A system message containing input text. */ + fun system(): Optional = Optional.ofNullable(system) + + /** An assistant message containing output text or refusals. */ + fun assistant(): Optional = Optional.ofNullable(assistant) + + fun isUser(): Boolean = user != null + + fun isDeveloper(): Boolean = developer != null + + fun isSystem(): Boolean = system != null + + fun isAssistant(): Boolean = assistant != null + + /** A user message containing input text, images, or JSON. */ + fun asUser(): User = user.getOrThrow("user") + + /** A developer message containing input text. */ + fun asDeveloper(): Developer = developer.getOrThrow("developer") + + /** A system message containing input text. */ + fun asSystem(): System = system.getOrThrow("system") + + /** An assistant message containing output text or refusals. */ + fun asAssistant(): Assistant = assistant.getOrThrow("assistant") + + fun _json(): Optional = Optional.ofNullable(_json) /** - * Expected to always return the following: + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: * ```java - * JsonValue.from("user") + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = decisionInputMessage.accept(new DecisionInputMessage.Visitor>() { + * @Override + * public Optional visitUser(User user) { + * return Optional.of(user.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); * ``` * - * However, this method can be useful for debugging and logging (e.g. if the server responded - * with an unexpected value). + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and + * the current variant is unknown. */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonValue = role + fun accept(visitor: Visitor): T = + when { + user != null -> visitor.visitUser(user) + developer != null -> visitor.visitDeveloper(developer) + system != null -> visitor.visitSystem(system) + assistant != null -> visitor.visitAssistant(assistant) + else -> visitor.unknown(_json) + } - /** - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") + private var validated: Boolean = false /** - * Returns the raw JSON value of [content]. + * Validates that the types of all values in this object match their expected types recursively. * - * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content - - /** - * Returns the raw JSON value of [type]. + * This method is _not_ forwards compatible with new types from the API for existing fields. * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + fun validate(): DecisionInputMessage = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitUser(user: User) { + user.validate() + } + + override fun visitDeveloper(developer: Developer) { + developer.validate() + } + + override fun visitSystem(system: System) { + system.validate() + } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + override fun visitAssistant(assistant: Assistant) { + assistant.validate() + } + } + ) + validated = true } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + /** - * Returns an immutable snapshot of all present JSON properties, including declared and - * additional properties. + * Returns a score indicating how many valid values are contained in this object recursively. * - * Keys use their JSON names. Declared properties take precedence over additional properties - * with the same name. Missing properties are omitted, while explicit nulls and unrecognized - * values are preserved. + * Used for best match union deserialization. */ - fun _allProperties(): Map = - collectAllProperties( - additionalProperties, - "content" to this.content, - "role" to this.role, - "type" to this.type, + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitUser(user: User) = user.validity() + + override fun visitDeveloper(developer: Developer) = developer.validity() + + override fun visitSystem(system: System) = system.validity() + + override fun visitAssistant(assistant: Assistant) = assistant.validity() + + override fun unknown(json: JsonValue?) = 0 + } ) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DecisionInputMessage && + user == other.user && + developer == other.developer && + system == other.system && + assistant == other.assistant + } + + override fun hashCode(): Int = Objects.hash(user, developer, system, assistant) + + override fun toString(): String = + when { + user != null -> "DecisionInputMessage{user=$user}" + developer != null -> "DecisionInputMessage{developer=$developer}" + system != null -> "DecisionInputMessage{system=$system}" + assistant != null -> "DecisionInputMessage{assistant=$assistant}" + _json != null -> "DecisionInputMessage{_unknown=$_json}" + else -> throw IllegalStateException("Invalid DecisionInputMessage") + } + + companion object { + + /** A user message containing input text, images, or JSON. */ + @JvmStatic fun ofUser(user: User) = DecisionInputMessage(user = user) + + /** A developer message containing input text. */ + @JvmStatic + fun ofDeveloper(developer: Developer) = DecisionInputMessage(developer = developer) + + /** A system message containing input text. */ + @JvmStatic fun ofSystem(system: System) = DecisionInputMessage(system = system) + + /** An assistant message containing output text or refusals. */ + @JvmStatic + fun ofAssistant(assistant: Assistant) = DecisionInputMessage(assistant = assistant) + } + /** - * Returns the legacy additional-properties view. - * - * @deprecated Use [_allProperties] to access both declared and additional JSON properties. This - * accessor retains its legacy additional-properties behavior. + * An interface that defines how to map each variant of [DecisionInputMessage] to a value of + * type [T]. */ - @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + interface Visitor { - fun toBuilder() = Builder().from(this) + /** A user message containing input text, images, or JSON. */ + fun visitUser(user: User): T - companion object { + /** A developer message containing input text. */ + fun visitDeveloper(developer: Developer): T + + /** A system message containing input text. */ + fun visitSystem(system: System): T + + /** An assistant message containing output text or refusals. */ + fun visitAssistant(assistant: Assistant): T /** - * Returns a mutable builder for constructing an instance of [DecisionInputMessage]. + * Maps an unknown variant of [DecisionInputMessage] to a value of type [T]. * - * The following fields are required: - * ```java - * .content() - * ``` + * An instance of [DecisionInputMessage] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. */ - @JvmStatic fun builder() = Builder() + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown DecisionInputMessage") + } } - /** A builder for [DecisionInputMessage]. */ - class Builder internal constructor() { + internal class Deserializer : + BaseDeserializer(DecisionInputMessage::class) { - private var content: JsonField? = null - private var role: JsonValue = JsonValue.from("user") - private var type: JsonField = JsonField.of(Type.MESSAGE) - private var additionalProperties: MutableMap = mutableMapOf() + override fun ObjectCodec.deserialize(node: JsonNode): DecisionInputMessage { + val json = JsonValue.fromJsonNode(node) + val role = json.asObject().getOrNull()?.get("role")?.asString()?.getOrNull() - @JvmSynthetic - internal fun from(decisionInputMessage: DecisionInputMessage) = apply { - content = decisionInputMessage.content - role = decisionInputMessage.role - type = decisionInputMessage.type - additionalProperties = decisionInputMessage.additionalProperties.toMutableMap() + when (role) { + "user" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputMessage(user = it, _json = json) + } ?: DecisionInputMessage(_json = json) + } + "developer" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputMessage(developer = it, _json = json) + } ?: DecisionInputMessage(_json = json) + } + "system" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputMessage(system = it, _json = json) + } ?: DecisionInputMessage(_json = json) + } + "assistant" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputMessage(assistant = it, _json = json) + } ?: DecisionInputMessage(_json = json) + } + } + + return DecisionInputMessage(_json = json) } + } - /** Text evidence or an ordered list of text and image parts. */ - fun content(content: Content) = content(JsonField.of(content)) + internal class Serializer : BaseSerializer(DecisionInputMessage::class) { - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [Content] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + override fun serialize( + value: DecisionInputMessage, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.user != null -> generator.writeObject(value.user) + value.developer != null -> generator.writeObject(value.developer) + value.system != null -> generator.writeObject(value.system) + value.assistant != null -> generator.writeObject(value.assistant) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid DecisionInputMessage") + } + } + } - /** Alias for calling [content] with `Content.ofString(string)`. */ - fun content(string: String) = content(Content.ofString(string)) + /** A user message containing input text, images, or JSON. */ + class User + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val content: JsonField, + private val role: JsonValue, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { - /** Alias for calling [content] with `Content.ofParts(parts)`. */ - fun contentOfParts(parts: List) = content(Content.ofParts(parts)) + @JsonCreator + private constructor( + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("role") @ExcludeMissing role: JsonValue = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(content, role, type, mutableMapOf()) /** - * Sets the field to an arbitrary JSON value. + * A text string or an ordered list of text, image, and JSON parts. * - * It is usually unnecessary to call this method because the field defaults to the - * following: + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun content(): Content = content.getRequired("content") + + /** + * Expected to always return the following: * ```java * JsonValue.from("user") * ``` * - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). */ - fun role(role: JsonValue) = apply { this.role = role } + @JsonProperty("role") @ExcludeMissing fun _role(): JsonValue = role - fun type(type: Type) = type(JsonField.of(type)) + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") /** - * Sets [Builder.type] to an arbitrary JSON value. + * Returns the raw JSON value of [content]. * - * You should usually call [Builder.type] with a well-typed [Type] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. */ - fun type(type: JsonField) = apply { this.type = type } + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type - fun putAdditionalProperty(key: String, value: JsonValue) = apply { + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) } - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } - - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } - - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } - /** - * Returns an immutable instance of [DecisionInputMessage]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * ``` + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. * - * @throws IllegalStateException if any required field is unset. + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. */ - fun build(): DecisionInputMessage = - DecisionInputMessage( - checkRequired("content", content), - role, - type, - additionalProperties.toMutableMap(), + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "content" to this.content, + "role" to this.role, + "type" to this.type, ) - } - private var validated: Boolean = false + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - /** - * Validates that the types of all values in this object match their expected types recursively. - * - * This method is _not_ forwards compatible with new types from the API for existing fields. - * - * @throws OpenAIInvalidDataException if any value type in this object doesn't match its - * expected type. - */ - fun validate(): DecisionInputMessage = apply { - if (validated) { - return@apply + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [User]. + * + * The following fields are required: + * ```java + * .content() + * ``` + */ + @JvmStatic fun builder() = Builder() } - content().validate() - _role().let { - if (it != JsonValue.from("user")) { - throw OpenAIInvalidDataException("'role' is invalid, received $it") + /** A builder for [User]. */ + class Builder internal constructor() { + + private var content: JsonField? = null + private var role: JsonValue = JsonValue.from("user") + private var type: JsonField = JsonField.of(Type.MESSAGE) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(user: User) = apply { + content = user.content + role = user.role + type = user.type + additionalProperties = user.additionalProperties.toMutableMap() } - } - type().ifPresent { it.validate() } - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** A text string or an ordered list of text, image, and JSON parts. */ + fun content(content: Content) = content(JsonField.of(content)) - /** - * Returns 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 } + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Text evidence or an ordered list of text and image parts. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val string: String? = null, - private val parts: List? = null, - private val _json: JsonValue? = null, - ) { + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [Content] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } - fun string(): Optional = Optional.ofNullable(string) + /** Alias for calling [content] with `Content.ofString(string)`. */ + fun content(string: String) = content(Content.ofString(string)) - fun parts(): Optional> = Optional.ofNullable(parts) + /** Alias for calling [content] with `Content.ofParts(parts)`. */ + fun contentOfParts(parts: List) = content(Content.ofParts(parts)) - fun isString(): Boolean = string != null + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("user") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun role(role: JsonValue) = apply { this.role = role } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } - fun isParts(): Boolean = parts != null + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun asString(): String = string.getOrThrow("string") + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun asParts(): List = parts.getOrThrow("parts") + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } - fun _json(): Optional = Optional.ofNullable(_json) + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } - /** - * Maps this instance's current variant to a value of type [T] using the given [visitor]. - * - * Note that this method is _not_ forwards compatible with new variants from the API, unless - * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of - * the SDK gracefully, consider overriding [Visitor.unknown]: - * ```java - * import com.openai.core.JsonValue; - * import java.util.Optional; - * - * Optional result = content.accept(new Content.Visitor>() { - * @Override - * public Optional visitString(String string) { - * return Optional.of(string.toString()); - * } - * - * // ... - * - * @Override - * public Optional unknown(JsonValue json) { - * // Or inspect the `json`. - * return Optional.empty(); - * } - * }); - * ``` - * - * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in [visitor] - * and the current variant is unknown. - */ - fun accept(visitor: Visitor): T = - when { - string != null -> visitor.visitString(string) - parts != null -> visitor.visitParts(parts) - else -> visitor.unknown(_json) + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } + /** + * Returns an immutable instance of [User]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): User = + User( + checkRequired("content", content), + role, + type, + additionalProperties.toMutableMap(), + ) + } + private var validated: Boolean = false /** @@ -347,20 +521,18 @@ private constructor( * @throws OpenAIInvalidDataException if any value type in this object doesn't match its * expected type. */ - fun validate(): Content = apply { + fun validate(): User = apply { if (validated) { return@apply } - accept( - object : Visitor { - override fun visitString(string: String) {} - - override fun visitParts(parts: List) { - parts.forEach { it.validate() } - } + content().validate() + _role().let { + if (it != JsonValue.from("user")) { + throw OpenAIInvalidDataException("'role' is invalid, received $it") } - ) + } + type().ifPresent { it.validate() } validated = true } @@ -380,192 +552,579 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - accept( - object : Visitor { - override fun visitString(string: String) = 1 - - override fun visitParts(parts: List) = - parts.sumOf { it.validity().toInt() } - - override fun unknown(json: JsonValue?) = 0 - } - ) + (content.asKnown().getOrNull()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("user")) 1 else 0 } + + (type.asKnown().getOrNull()?.validity() ?: 0) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + /** A text string or an ordered list of text, image, and JSON parts. */ + @JsonDeserialize(using = Content.Deserializer::class) + @JsonSerialize(using = Content.Serializer::class) + class Content + private constructor( + private val string: String? = null, + private val parts: List? = null, + private val _json: JsonValue? = null, + ) { - return other is Content && string == other.string && parts == other.parts - } + fun string(): Optional = Optional.ofNullable(string) - override fun hashCode(): Int = Objects.hash(string, parts) + fun parts(): Optional> = Optional.ofNullable(parts) - override fun toString(): String = - when { - string != null -> "Content{string=$string}" - parts != null -> "Content{parts=$parts}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } + fun isString(): Boolean = string != null - companion object { + fun isParts(): Boolean = parts != null - @JvmStatic fun ofString(string: String) = Content(string = string) + fun asString(): String = string.getOrThrow("string") - @JvmStatic - fun ofParts(parts: List) = Content(parts = parts.toImmutable()) - } + fun asParts(): List = parts.getOrThrow("parts") - /** - * An interface that defines how to map each variant of [Content] to a value of type [T]. - */ - interface Visitor { + fun _json(): Optional = Optional.ofNullable(_json) - fun visitString(string: String): T + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = content.accept(new Content.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + parts != null -> visitor.visitParts(parts) + else -> visitor.unknown(_json) + } - fun visitParts(parts: List): T + private var validated: Boolean = false /** - * Maps an unknown variant of [Content] to a value of type [T]. + * Validates that the types of all values in this object match their expected types + * recursively. * - * An instance of [Content] can contain an unknown variant if it was deserialized from - * data that doesn't match any known variant. For example, if the SDK is on an older - * version than the API, then the API may respond with new variants that the SDK is - * unaware of. + * This method is _not_ forwards compatible with new types from the API for existing + * fields. * - * @throws OpenAIInvalidDataException in the default implementation. + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. */ - fun unknown(json: JsonValue?): T { - throw OpenAIInvalidDataException("Unknown Content") + fun validate(): Content = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitParts(parts: List) { + parts.forEach { it.validate() } + } + } + ) + validated = true } - } - internal class Deserializer : BaseDeserializer(Content::class) { + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun ObjectCodec.deserialize(node: JsonNode): Content { - val json = JsonValue.fromJsonNode(node) + /** + * Returns 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 - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - Content(string = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef>())?.let { - Content(parts = 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 -> 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() + override fun visitParts(parts: List) = + parts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return other is Content && string == other.string && parts == other.parts } - } - internal class Serializer : BaseSerializer(Content::class) { + override fun hashCode(): Int = Objects.hash(string, parts) - override fun serialize( - value: Content, - generator: JsonGenerator, - provider: SerializerProvider, - ) { + override fun toString(): String = when { - value.string != null -> generator.writeObject(value.string) - value.parts != null -> generator.writeObject(value.parts) - value._json != null -> generator.writeObject(value._json) + string != null -> "Content{string=$string}" + parts != null -> "Content{parts=$parts}" + _json != null -> "Content{_unknown=$_json}" else -> throw IllegalStateException("Invalid Content") } - } - } - } - - class Type @JsonCreator private constructor(private val value: JsonField) : Enum { - /** - * Returns this class instance's raw value. - * - * This is usually only useful if this instance was deserialized from data that doesn't - * match any known member, and you want to know that value. For example, if the SDK is on an - * older version than the API, then the API may respond with new members that the SDK is - * unaware of. - */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + companion object { - companion object { + @JvmStatic fun ofString(string: String) = Content(string = string) - @JvmField val MESSAGE = of("message") + @JvmStatic + fun ofParts(parts: List) = Content(parts = parts.toImmutable()) + } - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** + * An interface that defines how to map each variant of [Content] to a value of type + * [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitParts(parts: List): T + + /** + * Maps an unknown variant of [Content] to a value of type [T]. + * + * An instance of [Content] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Content") + } + } - /** An enum containing [Type]'s known values. */ - enum class Known { - MESSAGE - } + internal class Deserializer : BaseDeserializer(Content::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Content { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Content(parts = 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 -> 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() + } + } + } + + internal class Serializer : BaseSerializer(Content::class) { + + override fun serialize( + value: Content, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.parts != null -> generator.writeObject(value.parts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Content") + } + } + } + } + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val MESSAGE = of("message") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + MESSAGE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + MESSAGE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + MESSAGE -> Value.MESSAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + MESSAGE -> Known.MESSAGE + else -> throw OpenAIInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is User && + content == other.content && + role == other.role && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(content, role, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "User{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" + } + + /** A developer message containing input text. */ + class Developer + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val content: JsonField, + private val role: JsonValue, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("role") @ExcludeMissing role: JsonValue = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(content, role, type, mutableMapOf()) /** - * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * A text string or an ordered list of input text parts. * - * An instance of [Type] can contain an unknown value in a couple of cases: - * - It was deserialized from data that doesn't match any known member. For example, if the - * SDK is on an older version than the API, then the API may respond with new members that - * the SDK is unaware of. - * - It was constructed with an arbitrary value using the [of] method. + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - enum class Value { - MESSAGE, - /** An enum member indicating that [Type] was instantiated with an unknown value. */ - _UNKNOWN, - } + fun content(): Content = content.getRequired("content") /** - * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] - * if the class was instantiated with an unknown value. + * Expected to always return the following: + * ```java + * JsonValue.from("developer") + * ``` * - * Use the [known] method instead if you're certain the value is always known or if you want - * to throw for the unknown case. + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). */ - fun value(): Value = - when (this) { - MESSAGE -> Value.MESSAGE - else -> Value._UNKNOWN - } + @JsonProperty("role") @ExcludeMissing fun _role(): JsonValue = role /** - * Returns an enum member corresponding to this class instance's value. + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [content]. * - * Use the [value] method instead if you're uncertain the value is always known and don't - * want to throw for the unknown case. + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [type]. * - * @throws OpenAIInvalidDataException if this class instance's value is a not a known - * member. + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. */ - fun known(): Known = - when (this) { - MESSAGE -> Known.MESSAGE - else -> throw OpenAIInvalidDataException("Unknown Type: $value") - } + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } /** - * Returns this class instance's primitive wire representation. + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. * - * This differs from the [toString] method because that method is primarily for debugging - * and generally doesn't throw. + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "content" to this.content, + "role" to this.role, + "type" to this.type, + ) + + /** + * Returns the legacy additional-properties view. * - * @throws OpenAIInvalidDataException if this class instance's value does not have the - * expected primitive type. + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. */ - fun asString(): String = - _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Developer]. + * + * The following fields are required: + * ```java + * .content() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Developer]. */ + class Builder internal constructor() { + + private var content: JsonField? = null + private var role: JsonValue = JsonValue.from("developer") + private var type: JsonField = JsonField.of(Type.MESSAGE) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(developer: Developer) = apply { + content = developer.content + role = developer.role + type = developer.type + additionalProperties = developer.additionalProperties.toMutableMap() + } + + /** A text string or an ordered list of input text parts. */ + fun content(content: Content) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [Content] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** Alias for calling [content] with `Content.ofString(string)`. */ + fun content(string: String) = content(Content.ofString(string)) + + /** Alias for calling [content] with `Content.ofParts(parts)`. */ + fun contentOfParts(parts: List) = content(Content.ofParts(parts)) + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("developer") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun role(role: JsonValue) = apply { this.role = role } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Developer]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Developer = + Developer( + checkRequired("content", content), + role, + type, + additionalProperties.toMutableMap(), + ) + } private var validated: Boolean = false @@ -578,12 +1137,18 @@ private constructor( * @throws OpenAIInvalidDataException if any value type in this object doesn't match its * expected type. */ - fun validate(): Type = apply { + fun validate(): Developer = apply { if (validated) { return@apply } - known() + content().validate() + _role().let { + if (it != JsonValue.from("developer")) { + throw OpenAIInvalidDataException("'role' is invalid, received $it") + } + } + type().ifPresent { it.validate() } validated = true } @@ -601,37 +1166,2499 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + @JvmSynthetic + internal fun validity(): Int = + (content.asKnown().getOrNull()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("developer")) 1 else 0 } + + (type.asKnown().getOrNull()?.validity() ?: 0) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** A text string or an ordered list of input text parts. */ + @JsonDeserialize(using = Content.Deserializer::class) + @JsonSerialize(using = Content.Serializer::class) + class Content + private constructor( + private val string: String? = null, + private val parts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun parts(): Optional> = Optional.ofNullable(parts) + + fun isString(): Boolean = string != null + + fun isParts(): Boolean = parts != null + + fun asString(): String = string.getOrThrow("string") + + fun asParts(): List = parts.getOrThrow("parts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = content.accept(new Content.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + parts != null -> visitor.visitParts(parts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Content = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitParts(parts: List) { + parts.forEach { it.validate() } + } + } + ) + validated = true } - return other is Type && value == other.value - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns 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 hashCode() = value.hashCode() + override fun visitParts(parts: List) = + parts.sumOf { it.validity().toInt() } - override fun toString() = value.toString() - } + override fun unknown(json: JsonValue?) = 0 + } + ) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Content && string == other.string && parts == other.parts + } + + override fun hashCode(): Int = Objects.hash(string, parts) + + override fun toString(): String = + when { + string != null -> "Content{string=$string}" + parts != null -> "Content{parts=$parts}" + _json != null -> "Content{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Content") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Content(string = string) + + @JvmStatic + fun ofParts(parts: List) = Content(parts = parts.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Content] to a value of type + * [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitParts(parts: List): T + + /** + * Maps an unknown variant of [Content] to a value of type [T]. + * + * An instance of [Content] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Content") + } + } + + internal class Deserializer : BaseDeserializer(Content::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Content { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Content(parts = 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 -> 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() + } + } + } + + internal class Serializer : BaseSerializer(Content::class) { + + override fun serialize( + value: Content, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.parts != null -> generator.writeObject(value.parts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Content") + } + } + } } - return other is DecisionInputMessage && - content == other.content && - role == other.role && - type == other.type && - additionalProperties == other.additionalProperties - } + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val MESSAGE = of("message") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + MESSAGE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + MESSAGE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + MESSAGE -> Value.MESSAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + MESSAGE -> Known.MESSAGE + else -> throw OpenAIInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun hashCode(): Int = hashCode + /** + * Returns a 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 toString() = - "DecisionInputMessage{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Developer && + content == other.content && + role == other.role && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(content, role, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Developer{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" + } + + /** A system message containing input text. */ + class System + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val content: JsonField, + private val role: JsonValue, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("role") @ExcludeMissing role: JsonValue = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(content, role, type, mutableMapOf()) + + /** + * A text string or an ordered list of input text parts. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun content(): Content = content.getRequired("content") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("system") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("role") @ExcludeMissing fun _role(): JsonValue = role + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "content" to this.content, + "role" to this.role, + "type" to this.type, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [System]. + * + * The following fields are required: + * ```java + * .content() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [System]. */ + class Builder internal constructor() { + + private var content: JsonField? = null + private var role: JsonValue = JsonValue.from("system") + private var type: JsonField = JsonField.of(Type.MESSAGE) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(system: System) = apply { + content = system.content + role = system.role + type = system.type + additionalProperties = system.additionalProperties.toMutableMap() + } + + /** A text string or an ordered list of input text parts. */ + fun content(content: Content) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [Content] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** Alias for calling [content] with `Content.ofString(string)`. */ + fun content(string: String) = content(Content.ofString(string)) + + /** Alias for calling [content] with `Content.ofParts(parts)`. */ + fun contentOfParts(parts: List) = content(Content.ofParts(parts)) + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("system") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun role(role: JsonValue) = apply { this.role = role } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [System]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): System = + System( + checkRequired("content", content), + role, + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): System = apply { + if (validated) { + return@apply + } + + content().validate() + _role().let { + if (it != JsonValue.from("system")) { + throw OpenAIInvalidDataException("'role' is invalid, received $it") + } + } + 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.let { if (it == JsonValue.from("system")) 1 else 0 } + + (type.asKnown().getOrNull()?.validity() ?: 0) + + /** A text string or an ordered list of input text parts. */ + @JsonDeserialize(using = Content.Deserializer::class) + @JsonSerialize(using = Content.Serializer::class) + class Content + private constructor( + private val string: String? = null, + private val parts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun parts(): Optional> = Optional.ofNullable(parts) + + fun isString(): Boolean = string != null + + fun isParts(): Boolean = parts != null + + fun asString(): String = string.getOrThrow("string") + + fun asParts(): List = parts.getOrThrow("parts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = content.accept(new Content.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + parts != null -> visitor.visitParts(parts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Content = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitParts(parts: List) { + parts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitParts(parts: List) = + parts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Content && string == other.string && parts == other.parts + } + + override fun hashCode(): Int = Objects.hash(string, parts) + + override fun toString(): String = + when { + string != null -> "Content{string=$string}" + parts != null -> "Content{parts=$parts}" + _json != null -> "Content{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Content") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Content(string = string) + + @JvmStatic + fun ofParts(parts: List) = Content(parts = parts.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Content] to a value of type + * [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitParts(parts: List): T + + /** + * Maps an unknown variant of [Content] to a value of type [T]. + * + * An instance of [Content] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Content") + } + } + + internal class Deserializer : BaseDeserializer(Content::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Content { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Content(parts = 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 -> 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() + } + } + } + + internal class Serializer : BaseSerializer(Content::class) { + + override fun serialize( + value: Content, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.parts != null -> generator.writeObject(value.parts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Content") + } + } + } + } + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val MESSAGE = of("message") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + MESSAGE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + MESSAGE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + MESSAGE -> Value.MESSAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + MESSAGE -> Known.MESSAGE + else -> throw OpenAIInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is System && + content == other.content && + role == other.role && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(content, role, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "System{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" + } + + /** An assistant message containing output text or refusals. */ + class Assistant + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val content: JsonField, + private val role: JsonValue, + private val phase: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("role") @ExcludeMissing role: JsonValue = JsonMissing.of(), + @JsonProperty("phase") @ExcludeMissing phase: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(content, role, phase, type, mutableMapOf()) + + /** + * A text string or an ordered list of output text and refusal parts. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun content(): Content = content.getRequired("content") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("assistant") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("role") @ExcludeMissing fun _role(): JsonValue = role + + /** + * The phase of an assistant message. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun phase(): Optional = phase.getOptional("phase") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [phase]. + * + * Unlike [phase], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("phase") @ExcludeMissing fun _phase(): JsonField = phase + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including declared and + * additional properties. + * + * Keys use their JSON names. Declared properties take precedence over additional properties + * with the same name. Missing properties are omitted, while explicit nulls and unrecognized + * values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "content" to this.content, + "role" to this.role, + "phase" to this.phase, + "type" to this.type, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON properties. + * This accessor retains its legacy additional-properties behavior. + */ + @Deprecated("Use _allProperties() to access both declared and additional JSON properties.") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Assistant]. + * + * The following fields are required: + * ```java + * .content() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Assistant]. */ + class Builder internal constructor() { + + private var content: JsonField? = null + private var role: JsonValue = JsonValue.from("assistant") + private var phase: JsonField = JsonMissing.of() + private var type: JsonField = JsonField.of(Type.MESSAGE) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(assistant: Assistant) = apply { + content = assistant.content + role = assistant.role + phase = assistant.phase + type = assistant.type + additionalProperties = assistant.additionalProperties.toMutableMap() + } + + /** A text string or an ordered list of output text and refusal parts. */ + fun content(content: Content) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [Content] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** Alias for calling [content] with `Content.ofString(string)`. */ + fun content(string: String) = content(Content.ofString(string)) + + /** Alias for calling [content] with `Content.ofParts(parts)`. */ + fun contentOfParts(parts: List) = + content(Content.ofParts(parts)) + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("assistant") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun role(role: JsonValue) = apply { this.role = role } + + /** The phase of an assistant message. */ + fun phase(phase: Phase?) = phase(JsonField.ofNullable(phase)) + + /** Alias for calling [Builder.phase] with `phase.orElse(null)`. */ + fun phase(phase: Optional) = phase(phase.getOrNull()) + + /** + * Sets [Builder.phase] to an arbitrary JSON value. + * + * You should usually call [Builder.phase] with a well-typed [Phase] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun phase(phase: JsonField) = apply { this.phase = phase } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Assistant]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Assistant = + Assistant( + checkRequired("content", content), + role, + phase, + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Assistant = apply { + if (validated) { + return@apply + } + + content().validate() + _role().let { + if (it != JsonValue.from("assistant")) { + throw OpenAIInvalidDataException("'role' is invalid, received $it") + } + } + phase().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()?.validity() ?: 0) + + role.let { if (it == JsonValue.from("assistant")) 1 else 0 } + + (phase.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + /** A text string or an ordered list of output text and refusal parts. */ + @JsonDeserialize(using = Content.Deserializer::class) + @JsonSerialize(using = Content.Serializer::class) + class Content + private constructor( + private val string: String? = null, + private val parts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun parts(): Optional> = Optional.ofNullable(parts) + + fun isString(): Boolean = string != null + + fun isParts(): Boolean = parts != null + + fun asString(): String = string.getOrThrow("string") + + fun asParts(): List = parts.getOrThrow("parts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = content.accept(new Content.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + parts != null -> visitor.visitParts(parts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Content = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitParts(parts: List) { + parts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitParts(parts: List) = + parts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Content && string == other.string && parts == other.parts + } + + override fun hashCode(): Int = Objects.hash(string, parts) + + override fun toString(): String = + when { + string != null -> "Content{string=$string}" + parts != null -> "Content{parts=$parts}" + _json != null -> "Content{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Content") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Content(string = string) + + @JvmStatic + fun ofParts(parts: List) = + Content(parts = parts.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Content] to a value of type + * [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitParts(parts: List): T + + /** + * Maps an unknown variant of [Content] to a value of type [T]. + * + * An instance of [Content] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Content") + } + } + + internal class Deserializer : BaseDeserializer(Content::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Content { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { Content(parts = 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 -> 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() + } + } + } + + internal class Serializer : BaseSerializer(Content::class) { + + override fun serialize( + value: Content, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.parts != null -> generator.writeObject(value.parts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Content") + } + } + } + + @JsonDeserialize(using = UnnamedSchemaWithArrayParent0.Deserializer::class) + @JsonSerialize(using = UnnamedSchemaWithArrayParent0.Serializer::class) + class UnnamedSchemaWithArrayParent0 + private constructor( + private val outputText: OutputText? = null, + private val refusal: Refusal? = null, + private val _json: JsonValue? = null, + ) { + + fun outputText(): Optional = Optional.ofNullable(outputText) + + fun refusal(): Optional = Optional.ofNullable(refusal) + + fun isOutputText(): Boolean = outputText != null + + fun isRefusal(): Boolean = refusal != null + + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + fun asRefusal(): Refusal = refusal.getOrThrow("refusal") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = unnamedSchemaWithArrayParent0.accept(new UnnamedSchemaWithArrayParent0.Visitor>() { + * @Override + * public Optional visitOutputText(OutputText outputText) { + * return Optional.of(outputText.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + 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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): UnnamedSchemaWithArrayParent0 = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + + override fun visitRefusal(refusal: Refusal) { + refusal.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 visitOutputText(outputText: OutputText) = + outputText.validity() + + override fun visitRefusal(refusal: Refusal) = refusal.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UnnamedSchemaWithArrayParent0 && + outputText == other.outputText && + refusal == other.refusal + } + + override fun hashCode(): Int = Objects.hash(outputText, refusal) + + override fun toString(): String = + when { + outputText != null -> + "UnnamedSchemaWithArrayParent0{outputText=$outputText}" + refusal != null -> "UnnamedSchemaWithArrayParent0{refusal=$refusal}" + _json != null -> "UnnamedSchemaWithArrayParent0{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent0") + } + + companion object { + + @JvmStatic + fun ofOutputText(outputText: OutputText) = + UnnamedSchemaWithArrayParent0(outputText = outputText) + + @JvmStatic + fun ofRefusal(refusal: Refusal) = + UnnamedSchemaWithArrayParent0(refusal = refusal) + } + + /** + * An interface that defines how to map each variant of + * [UnnamedSchemaWithArrayParent0] to a value of type [T]. + */ + interface Visitor { + + fun visitOutputText(outputText: OutputText): T + + fun visitRefusal(refusal: Refusal): T + + /** + * Maps an unknown variant of [UnnamedSchemaWithArrayParent0] to a value of type + * [T]. + * + * An instance of [UnnamedSchemaWithArrayParent0] can contain an unknown variant + * if it was deserialized from data that doesn't match any known variant. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown UnnamedSchemaWithArrayParent0") + } + } + + internal class Deserializer : + BaseDeserializer( + UnnamedSchemaWithArrayParent0::class + ) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): UnnamedSchemaWithArrayParent0 { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "output_text" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + UnnamedSchemaWithArrayParent0(outputText = it, _json = json) + } ?: UnnamedSchemaWithArrayParent0(_json = json) + } + "refusal" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + UnnamedSchemaWithArrayParent0(refusal = it, _json = json) + } ?: UnnamedSchemaWithArrayParent0(_json = json) + } + } + + return UnnamedSchemaWithArrayParent0(_json = json) + } + } + + internal class Serializer : + BaseSerializer( + UnnamedSchemaWithArrayParent0::class + ) { + + override fun serialize( + value: UnnamedSchemaWithArrayParent0, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.outputText != null -> generator.writeObject(value.outputText) + value.refusal != null -> generator.writeObject(value.refusal) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent0") + } + } + } + + class OutputText + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun text(): String = text.getRequired("text") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including + * declared and additional properties. + * + * Keys use their JSON names. Declared properties take precedence over + * additional properties with the same name. Missing properties are omitted, + * while explicit nulls and unrecognized values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "text" to this.text, + "type" to this.type, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON + * properties. This accessor retains its legacy additional-properties + * behavior. + */ + @Deprecated( + "Use _allProperties() to access both declared and additional JSON properties." + ) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("output_text") + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = outputText.additionalProperties.toMutableMap() + } + + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults + * to the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties( + additionalProperties: Map + ) = apply { this.additionalProperties.putAll(additionalProperties) } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [OutputText]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("output_text")) 1 else 0 } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputText && + text == other.text && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(text, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + + class Refusal + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val refusal: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("refusal") + @ExcludeMissing + refusal: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(refusal, type, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun refusal(): String = refusal.getRequired("refusal") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("refusal") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [refusal]. + * + * Unlike [refusal], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("refusal") + @ExcludeMissing + fun _refusal(): JsonField = refusal + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + /** + * Returns an immutable snapshot of all present JSON properties, including + * declared and additional properties. + * + * Keys use their JSON names. Declared properties take precedence over + * additional properties with the same name. Missing properties are omitted, + * while explicit nulls and unrecognized values are preserved. + */ + fun _allProperties(): Map = + collectAllProperties( + additionalProperties, + "refusal" to this.refusal, + "type" to this.type, + ) + + /** + * Returns the legacy additional-properties view. + * + * @deprecated Use [_allProperties] to access both declared and additional JSON + * properties. This accessor retains its legacy additional-properties + * behavior. + */ + @Deprecated( + "Use _allProperties() to access both declared and additional JSON properties." + ) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Refusal]. + * + * The following fields are required: + * ```java + * .refusal() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Refusal]. */ + class Builder internal constructor() { + + private var refusal: JsonField? = null + private var type: JsonValue = JsonValue.from("refusal") + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(refusal: Refusal) = apply { + this.refusal = refusal.refusal + type = refusal.type + additionalProperties = refusal.additionalProperties.toMutableMap() + } + + fun refusal(refusal: String) = refusal(JsonField.of(refusal)) + + /** + * Sets [Builder.refusal] to an arbitrary JSON value. + * + * You should usually call [Builder.refusal] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun refusal(refusal: JsonField) = apply { this.refusal = refusal } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults + * to the following: + * ```java + * JsonValue.from("refusal") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties( + additionalProperties: Map + ) = apply { this.additionalProperties.putAll(additionalProperties) } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Refusal]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .refusal() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Refusal = + Refusal( + checkRequired("refusal", refusal), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Refusal = apply { + if (validated) { + return@apply + } + + refusal() + _type().let { + if (it != JsonValue.from("refusal")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (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 + } + + return other is Refusal && + refusal == other.refusal && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(refusal, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Refusal{refusal=$refusal, type=$type, additionalProperties=$additionalProperties}" + } + } + } + + /** The phase of an assistant message. */ + class Phase @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val COMMENTARY = of("commentary") + + @JvmField val FINAL_ANSWER = of("final_answer") + + @JvmStatic fun of(value: String) = Phase(JsonField.of(value)) + } + + /** An enum containing [Phase]'s known values. */ + enum class Known { + COMMENTARY, + FINAL_ANSWER, + } + + /** + * An enum containing [Phase]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Phase] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + COMMENTARY, + FINAL_ANSWER, + /** + * An enum member indicating that [Phase] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + COMMENTARY -> Value.COMMENTARY + FINAL_ANSWER -> Value.FINAL_ANSWER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + COMMENTARY -> Known.COMMENTARY + FINAL_ANSWER -> Known.FINAL_ANSWER + else -> throw OpenAIInvalidDataException("Unknown Phase: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Phase = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Phase && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val MESSAGE = of("message") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + MESSAGE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + MESSAGE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + MESSAGE -> Value.MESSAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + MESSAGE -> Known.MESSAGE + else -> throw OpenAIInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenAIInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Assistant && + content == other.content && + role == other.role && + phase == other.phase && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(content, role, phase, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Assistant{content=$content, role=$role, phase=$phase, type=$type, additionalProperties=$additionalProperties}" + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputPart.kt b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputPart.kt index 3cee3afb4..8dceaaa8f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputPart.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputPart.kt @@ -14,6 +14,7 @@ import com.openai.core.BaseSerializer import com.openai.core.JsonValue import com.openai.core.getOrThrow import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.DecisionInputJson import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull @@ -28,6 +29,7 @@ class DecisionInputPart private constructor( private val inputText: DecisionInputText? = null, private val inputImage: DecisionInputImage? = null, + private val inputJson: DecisionInputJson? = null, private val _json: JsonValue? = null, ) { @@ -39,10 +41,18 @@ private constructor( */ fun inputImage(): Optional = Optional.ofNullable(inputImage) + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON value. + * Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + fun inputJson(): Optional = Optional.ofNullable(inputJson) + fun isInputText(): Boolean = inputText != null fun isInputImage(): Boolean = inputImage != null + fun isInputJson(): Boolean = inputJson != null + fun asInputText(): DecisionInputText = inputText.getOrThrow("inputText") /** @@ -51,6 +61,12 @@ private constructor( */ fun asInputImage(): DecisionInputImage = inputImage.getOrThrow("inputImage") + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON value. + * Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + fun asInputJson(): DecisionInputJson = inputJson.getOrThrow("inputJson") + fun _json(): Optional = Optional.ofNullable(_json) /** @@ -86,6 +102,7 @@ private constructor( when { inputText != null -> visitor.visitInputText(inputText) inputImage != null -> visitor.visitInputImage(inputImage) + inputJson != null -> visitor.visitInputJson(inputJson) else -> visitor.unknown(_json) } @@ -113,6 +130,10 @@ private constructor( override fun visitInputImage(inputImage: DecisionInputImage) { inputImage.validate() } + + override fun visitInputJson(inputJson: DecisionInputJson) { + inputJson.validate() + } } ) validated = true @@ -139,6 +160,8 @@ private constructor( override fun visitInputImage(inputImage: DecisionInputImage) = inputImage.validity() + override fun visitInputJson(inputJson: DecisionInputJson) = inputJson.validity() + override fun unknown(json: JsonValue?) = 0 } ) @@ -150,15 +173,17 @@ private constructor( return other is DecisionInputPart && inputText == other.inputText && - inputImage == other.inputImage + inputImage == other.inputImage && + inputJson == other.inputJson } - override fun hashCode(): Int = Objects.hash(inputText, inputImage) + override fun hashCode(): Int = Objects.hash(inputText, inputImage, inputJson) override fun toString(): String = when { inputText != null -> "DecisionInputPart{inputText=$inputText}" inputImage != null -> "DecisionInputPart{inputImage=$inputImage}" + inputJson != null -> "DecisionInputPart{inputJson=$inputJson}" _json != null -> "DecisionInputPart{_unknown=$_json}" else -> throw IllegalStateException("Invalid DecisionInputPart") } @@ -175,6 +200,13 @@ private constructor( @JvmStatic fun ofInputImage(inputImage: DecisionInputImage) = DecisionInputPart(inputImage = inputImage) + + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON + * value. Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + @JvmStatic + fun ofInputJson(inputJson: DecisionInputJson) = DecisionInputPart(inputJson = inputJson) } /** @@ -191,6 +223,12 @@ private constructor( */ fun visitInputImage(inputImage: DecisionInputImage): T + /** + * A JSON object or an array of objects rendered as text. Nested values may be any JSON + * value. Limited to 32 container levels, 10,000 values, and 256 KiB of compact UTF-8 JSON. + */ + fun visitInputJson(inputJson: DecisionInputJson): T + /** * Maps an unknown variant of [DecisionInputPart] to a value of type [T]. * @@ -223,6 +261,11 @@ private constructor( DecisionInputPart(inputImage = it, _json = json) } ?: DecisionInputPart(_json = json) } + "input_json" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DecisionInputPart(inputJson = it, _json = json) + } ?: DecisionInputPart(_json = json) + } } return DecisionInputPart(_json = json) @@ -239,6 +282,7 @@ private constructor( when { value.inputText != null -> generator.writeObject(value.inputText) value.inputImage != null -> generator.writeObject(value.inputImage) + value.inputJson != null -> generator.writeObject(value.inputJson) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid DecisionInputPart") } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionTrigger.kt b/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionTrigger.kt index 3dbe19643..7ea947062 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionTrigger.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionTrigger.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.checkKnown import com.openai.core.checkRequired import com.openai.core.collectAllProperties import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.DecisionQuestionInputPart import com.openai.models.decisions.DecisionChoiceOption import java.util.Collections import java.util.Objects @@ -416,9 +418,29 @@ private constructor( * .build() * ``` */ - fun addPredicateQuestion(instructions: String) = + fun addPredicateQuestion(instructions: Question.Predicate.Instructions) = addQuestion(Question.Predicate.builder().instructions(instructions).build()) + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofString(string)`. + */ + fun addPredicateQuestion(string: String) = + addPredicateQuestion(Question.Predicate.Instructions.ofString(string)) + + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun addPredicateQuestionOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + addPredicateQuestion( + Question.Predicate.Instructions.ofDecisionQuestionInputParts( + decisionQuestionInputParts + ) + ) + /** Alias for calling [addQuestion] with `Question.ofChoice(choice)`. */ fun addQuestion(choice: Question.Choice) = addQuestion(Question.ofChoice(choice)) @@ -752,7 +774,7 @@ private constructor( class Predicate @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -762,7 +784,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(instructions, type, name, mutableMapOf()) @@ -772,7 +794,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `predicate`. @@ -801,7 +823,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -864,7 +886,7 @@ private constructor( /** A builder for [Predicate]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("predicate") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -877,20 +899,34 @@ private constructor( additionalProperties = predicate.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = + fun instructions(instructions: Instructions) = instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. + * You should usually call [Builder.instructions] with a well-typed + * [Instructions] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -976,7 +1012,7 @@ private constructor( return@apply } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("predicate")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -1002,10 +1038,253 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("predicate")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not + * known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = + decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a + * value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, + * if the SDK is on an older version than the API, then the API may respond + * with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : + BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then + // use the first completely valid match, or simply the first match + // if none are completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1033,7 +1312,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val choices: JsonField>, - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -1046,7 +1325,7 @@ private constructor( choices: JsonField> = JsonMissing.of(), @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(choices, instructions, type, name, mutableMapOf()) @@ -1065,7 +1344,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `choice`. @@ -1104,7 +1383,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -1170,7 +1449,7 @@ private constructor( class Builder internal constructor() { private var choices: JsonField>? = null - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("choice") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -1211,20 +1490,34 @@ private constructor( } } - fun instructions(instructions: String) = + fun instructions(instructions: Instructions) = instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. + * You should usually call [Builder.instructions] with a well-typed + * [Instructions] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -1313,7 +1606,7 @@ private constructor( } choices().forEach { it.validate() } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("choice")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -1340,10 +1633,253 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("choice")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not + * known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = + decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a + * value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, + * if the SDK is on an older version than the API, then the API may respond + * with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : + BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then + // use the first completely valid match, or simply the first match + // if none are completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1371,7 +1907,7 @@ private constructor( class Score @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val levels: JsonField>, private val type: JsonValue, private val name: JsonField, @@ -1382,7 +1918,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("levels") @ExcludeMissing levels: JsonField> = JsonMissing.of(), @@ -1395,7 +1931,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -1431,7 +1967,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [levels]. @@ -1506,7 +2042,7 @@ private constructor( /** A builder for [Score]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var levels: JsonField>? = null private var type: JsonValue = JsonValue.from("score") private var name: JsonField = JsonMissing.of() @@ -1521,20 +2057,34 @@ private constructor( additionalProperties = score.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = + fun instructions(instructions: Instructions) = instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. + * You should usually call [Builder.instructions] with a well-typed + * [Instructions] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + fun levels(levels: List) = levels(JsonField.of(levels)) /** @@ -1647,7 +2197,7 @@ private constructor( return@apply } - instructions() + instructions().validate() levels().forEach { it.validate() } _type().let { if (it != JsonValue.from("score")) { @@ -1674,11 +2224,254 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + (levels.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + type.let { if (it == JsonValue.from("score")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not + * known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = + decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a + * value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, + * if the SDK is on an older version than the API, then the API may respond + * with new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : + BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then + // use the first completely valid match, or simply the first match + // if none are completely valid. + else -> + bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + class Level @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( diff --git a/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationConfig.kt b/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationConfig.kt index c7198b6a6..1ab50a8c7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationConfig.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationConfig.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.checkKnown import com.openai.core.checkRequired import com.openai.core.collectAllProperties import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.DecisionQuestionInputPart import com.openai.models.decisions.DecisionChoiceOption import java.util.Collections import java.util.Objects @@ -187,9 +189,29 @@ private constructor( * .build() * ``` */ - fun addPredicateQuestion(instructions: String) = + fun addPredicateQuestion(instructions: Question.Predicate.Instructions) = addQuestion(Question.Predicate.builder().instructions(instructions).build()) + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofString(string)`. + */ + fun addPredicateQuestion(string: String) = + addPredicateQuestion(Question.Predicate.Instructions.ofString(string)) + + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun addPredicateQuestionOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + addPredicateQuestion( + Question.Predicate.Instructions.ofDecisionQuestionInputParts( + decisionQuestionInputParts + ) + ) + /** Alias for calling [addQuestion] with `Question.ofChoice(choice)`. */ fun addQuestion(choice: Question.Choice) = addQuestion(Question.ofChoice(choice)) @@ -522,7 +544,7 @@ private constructor( class Predicate @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -532,7 +554,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(instructions, type, name, mutableMapOf()) @@ -542,7 +564,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `predicate`. @@ -571,7 +593,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -633,7 +655,7 @@ private constructor( /** A builder for [Predicate]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("predicate") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -646,19 +668,34 @@ private constructor( additionalProperties = predicate.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -744,7 +781,7 @@ private constructor( return@apply } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("predicate")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -770,10 +807,249 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("predicate")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -801,7 +1077,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val choices: JsonField>, - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -814,7 +1090,7 @@ private constructor( choices: JsonField> = JsonMissing.of(), @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(choices, instructions, type, name, mutableMapOf()) @@ -833,7 +1109,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `choice`. @@ -871,7 +1147,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -936,7 +1212,7 @@ private constructor( class Builder internal constructor() { private var choices: JsonField>? = null - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("choice") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -976,19 +1252,34 @@ private constructor( } } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -1077,7 +1368,7 @@ private constructor( } choices().forEach { it.validate() } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("choice")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -1104,10 +1395,249 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("choice")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1135,7 +1665,7 @@ private constructor( class Score @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val levels: JsonField>, private val type: JsonValue, private val name: JsonField, @@ -1146,7 +1676,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("levels") @ExcludeMissing levels: JsonField> = JsonMissing.of(), @@ -1159,7 +1689,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -1195,7 +1725,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [levels]. @@ -1266,7 +1796,7 @@ private constructor( /** A builder for [Score]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var levels: JsonField>? = null private var type: JsonValue = JsonValue.from("score") private var name: JsonField = JsonMissing.of() @@ -1281,19 +1811,34 @@ private constructor( additionalProperties = score.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + fun levels(levels: List) = levels(JsonField.of(levels)) /** @@ -1406,7 +1951,7 @@ private constructor( return@apply } - instructions() + instructions().validate() levels().forEach { it.validate() } _type().let { if (it != JsonValue.from("score")) { @@ -1433,11 +1978,250 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + (levels.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + type.let { if (it == JsonValue.from("score")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + class Level @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( diff --git a/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfig.kt b/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfig.kt index d66c6b81e..6b2ae5e85 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfig.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfig.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.checkKnown import com.openai.core.checkRequired import com.openai.core.collectAllProperties import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.DecisionQuestionInputPart import com.openai.models.decisions.DecisionChoiceOption import java.util.Collections import java.util.Objects @@ -184,9 +186,29 @@ private constructor( * .build() * ``` */ - fun addPredicateQuestion(instructions: String) = + fun addPredicateQuestion(instructions: Question.Predicate.Instructions) = addQuestion(Question.Predicate.builder().instructions(instructions).build()) + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofString(string)`. + */ + fun addPredicateQuestion(string: String) = + addPredicateQuestion(Question.Predicate.Instructions.ofString(string)) + + /** + * Alias for calling [addPredicateQuestion] with + * `Question.Predicate.Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun addPredicateQuestionOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + addPredicateQuestion( + Question.Predicate.Instructions.ofDecisionQuestionInputParts( + decisionQuestionInputParts + ) + ) + /** Alias for calling [addQuestion] with `Question.ofChoice(choice)`. */ fun addQuestion(choice: Question.Choice) = addQuestion(Question.ofChoice(choice)) @@ -511,7 +533,7 @@ private constructor( class Predicate @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -521,7 +543,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(instructions, type, name, mutableMapOf()) @@ -531,7 +553,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `predicate`. @@ -560,7 +582,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -622,7 +644,7 @@ private constructor( /** A builder for [Predicate]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("predicate") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -635,19 +657,34 @@ private constructor( additionalProperties = predicate.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -733,7 +770,7 @@ private constructor( return@apply } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("predicate")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -759,10 +796,249 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("predicate")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -790,7 +1066,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val choices: JsonField>, - private val instructions: JsonField, + private val instructions: JsonField, private val type: JsonValue, private val name: JsonField, private val additionalProperties: MutableMap, @@ -803,7 +1079,7 @@ private constructor( choices: JsonField> = JsonMissing.of(), @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), ) : this(choices, instructions, type, name, mutableMapOf()) @@ -822,7 +1098,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * The type of the object. Always `choice`. @@ -860,7 +1136,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [name]. @@ -925,7 +1201,7 @@ private constructor( class Builder internal constructor() { private var choices: JsonField>? = null - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var type: JsonValue = JsonValue.from("choice") private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -965,19 +1241,34 @@ private constructor( } } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + /** * Sets the field to an arbitrary JSON value. * @@ -1066,7 +1357,7 @@ private constructor( } choices().forEach { it.validate() } - instructions() + instructions().validate() _type().let { if (it != JsonValue.from("choice")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -1093,10 +1384,249 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (choices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("choice")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1124,7 +1654,7 @@ private constructor( class Score @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val instructions: JsonField, + private val instructions: JsonField, private val levels: JsonField>, private val type: JsonValue, private val name: JsonField, @@ -1135,7 +1665,7 @@ private constructor( private constructor( @JsonProperty("instructions") @ExcludeMissing - instructions: JsonField = JsonMissing.of(), + instructions: JsonField = JsonMissing.of(), @JsonProperty("levels") @ExcludeMissing levels: JsonField> = JsonMissing.of(), @@ -1148,7 +1678,7 @@ private constructor( * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun instructions(): String = instructions.getRequired("instructions") + fun instructions(): Instructions = instructions.getRequired("instructions") /** * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is @@ -1184,7 +1714,7 @@ private constructor( */ @JsonProperty("instructions") @ExcludeMissing - fun _instructions(): JsonField = instructions + fun _instructions(): JsonField = instructions /** * Returns the raw JSON value of [levels]. @@ -1255,7 +1785,7 @@ private constructor( /** A builder for [Score]. */ class Builder internal constructor() { - private var instructions: JsonField? = null + private var instructions: JsonField? = null private var levels: JsonField>? = null private var type: JsonValue = JsonValue.from("score") private var name: JsonField = JsonMissing.of() @@ -1270,19 +1800,34 @@ private constructor( additionalProperties = score.additionalProperties.toMutableMap() } - fun instructions(instructions: String) = instructions(JsonField.of(instructions)) + fun instructions(instructions: Instructions) = + instructions(JsonField.of(instructions)) /** * Sets [Builder.instructions] to an arbitrary JSON value. * - * You should usually call [Builder.instructions] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * You should usually call [Builder.instructions] with a well-typed [Instructions] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. */ - fun instructions(instructions: JsonField) = apply { + fun instructions(instructions: JsonField) = apply { this.instructions = instructions } + /** Alias for calling [instructions] with `Instructions.ofString(string)`. */ + fun instructions(string: String) = instructions(Instructions.ofString(string)) + + /** + * Alias for calling [instructions] with + * `Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts)`. + */ + fun instructionsOfDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + instructions( + Instructions.ofDecisionQuestionInputParts(decisionQuestionInputParts) + ) + fun levels(levels: List) = levels(JsonField.of(levels)) /** @@ -1395,7 +1940,7 @@ private constructor( return@apply } - instructions() + instructions().validate() levels().forEach { it.validate() } _type().let { if (it != JsonValue.from("score")) { @@ -1422,11 +1967,250 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (instructions.asKnown().isPresent) 1 else 0) + + (instructions.asKnown().getOrNull()?.validity() ?: 0) + (levels.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + type.let { if (it == JsonValue.from("score")) 1 else 0 } + (if (name.asKnown().isPresent) 1 else 0) + @JsonDeserialize(using = Instructions.Deserializer::class) + @JsonSerialize(using = Instructions.Serializer::class) + class Instructions + private constructor( + private val string: String? = null, + private val decisionQuestionInputParts: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun decisionQuestionInputParts(): Optional> = + Optional.ofNullable(decisionQuestionInputParts) + + fun isString(): Boolean = string != null + + fun isDecisionQuestionInputParts(): Boolean = decisionQuestionInputParts != null + + fun asString(): String = string.getOrThrow("string") + + fun asDecisionQuestionInputParts(): List = + decisionQuestionInputParts.getOrThrow("decisionQuestionInputParts") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.openai.core.JsonValue; + * import java.util.Optional; + * + * Optional result = instructions.accept(new Instructions.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws OpenAIInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + decisionQuestionInputParts != null -> + visitor.visitDecisionQuestionInputParts(decisionQuestionInputParts) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws OpenAIInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Instructions = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) { + decisionQuestionInputParts.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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = decisionQuestionInputParts.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Instructions && + string == other.string && + decisionQuestionInputParts == other.decisionQuestionInputParts + } + + override fun hashCode(): Int = Objects.hash(string, decisionQuestionInputParts) + + override fun toString(): String = + when { + string != null -> "Instructions{string=$string}" + decisionQuestionInputParts != null -> + "Instructions{decisionQuestionInputParts=$decisionQuestionInputParts}" + _json != null -> "Instructions{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Instructions") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Instructions(string = string) + + @JvmStatic + fun ofDecisionQuestionInputParts( + decisionQuestionInputParts: List + ) = + Instructions( + decisionQuestionInputParts = decisionQuestionInputParts.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Instructions] to a value of + * type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitDecisionQuestionInputParts( + decisionQuestionInputParts: List + ): T + + /** + * Maps an unknown variant of [Instructions] to a value of type [T]. + * + * An instance of [Instructions] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if + * the SDK is on an older version than the API, then the API may respond with + * new variants that the SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown Instructions") + } + } + + internal class Deserializer : BaseDeserializer(Instructions::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Instructions { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Instructions(string = it, _json = json) + }, + tryDeserialize( + node, + jacksonTypeRef>(), + ) + ?.let { + Instructions( + decisionQuestionInputParts = 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 -> Instructions(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Instructions::class) { + + override fun serialize( + value: Instructions, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.decisionQuestionInputParts != null -> + generator.writeObject(value.decisionQuestionInputParts) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Instructions") + } + } + } + } + class Level @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( diff --git a/openai-java-core/src/test/kotlin/com/openai/core/DecisionsInputContractTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/DecisionsInputContractTest.kt new file mode 100644 index 000000000..7959028ab --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/core/DecisionsInputContractTest.kt @@ -0,0 +1,56 @@ +package com.openai.core + +import com.fasterxml.jackson.databind.JsonNode +import com.openai.models.DecisionInputJson +import com.openai.models.decisions.DecisionCreateParams +import com.openai.models.decisions.DecisionInputItem +import com.openai.models.decisions.DecisionInputMessage +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +internal class DecisionsInputContractTest { + @Test + fun serializesUserMessageInCreateRequest() { + val message = + DecisionInputMessage.ofUser( + DecisionInputMessage.User.builder().content("Review this request").build() + ) + val params = + DecisionCreateParams.builder() + .inputOfDecisionInputItems( + listOf(DecisionInputItem.ofDecisionInputMessage(message)) + ) + .model("model") + .addPredicateQuestion("Is this a request?") + .build() + + val input = jsonMapper().valueToTree(params._body()).path("input").path(0) + assertThat(input.path("role").asText()).isEqualTo("user") + assertThat(input.path("content").asText()).isEqualTo("Review this request") + } + + @Test + fun snapshotsJsonInputMaps() { + val original = JsonValue.from("original") + val source = mutableMapOf("answer" to original) + val input = DecisionInputJson.builder().dataOfMaps(listOf(source)).build() + val expected = + DecisionInputJson.builder().dataOfMaps(listOf(mapOf("answer" to original))).build() + val originalHashCode = input.hashCode() + + source["answer"] = JsonValue.from("changed") + val serialized = jsonMapper().writeValueAsString(input) + assertThat(jsonMapper().readTree(serialized).path("data").path(0).path("answer").asText()) + .isEqualTo("original") + val roundtripped = jsonMapper().readValue(serialized, DecisionInputJson::class.java) + + for (model in listOf(input, roundtripped)) { + val returned = model.data().asMaps().single() as MutableMap + assertThatThrownBy { returned["answer"] = JsonValue.from("changed") } + .isInstanceOf(UnsupportedOperationException::class.java) + assertThat(model).isEqualTo(expected) + assertThat(model.hashCode()).isEqualTo(originalHashCode).isEqualTo(expected.hashCode()) + } + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/DecisionInputJsonTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/DecisionInputJsonTest.kt new file mode 100644 index 000000000..e7f770894 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/DecisionInputJsonTest.kt @@ -0,0 +1,36 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +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 DecisionInputJsonTest { + + @Test + fun createAndRoundtrip() { + val decisionInputJson = + DecisionInputJson.builder() + .data(DecisionInputJson.Data.UnionMember0.builder().build()) + .build() + + assertThat(decisionInputJson.data()) + .isEqualTo( + DecisionInputJson.Data.ofUnionMember0( + DecisionInputJson.Data.UnionMember0.builder().build() + ) + ) + + val jsonMapper = jsonMapper() + + val roundtrippedDecisionInputJson = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputJson), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputJson).isEqualTo(decisionInputJson) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputImageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputImageTest.kt new file mode 100644 index 000000000..6074070e1 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputImageTest.kt @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +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 DecisionQuestionInputImageTest { + + @Test + fun createAndRoundtrip() { + val decisionQuestionInputImage = + DecisionQuestionInputImage.builder() + .imageUrl("https://") + .detail(DecisionQuestionInputImage.Detail.LOW) + .build() + + assertThat(decisionQuestionInputImage.imageUrl()).isEqualTo("https://") + assertThat(decisionQuestionInputImage.detail()) + .contains(DecisionQuestionInputImage.Detail.LOW) + + val jsonMapper = jsonMapper() + + val roundtrippedDecisionQuestionInputImage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionQuestionInputImage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionQuestionInputImage).isEqualTo(decisionQuestionInputImage) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputPartTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputPartTest.kt new file mode 100644 index 000000000..6098e88a0 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputPartTest.kt @@ -0,0 +1,130 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +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 DecisionQuestionInputPartTest { + + @Test + fun ofInputText() { + val inputText = DecisionQuestionInputText.builder().text("text").build() + + val decisionQuestionInputPart = DecisionQuestionInputPart.ofInputText(inputText) + + assertThat(decisionQuestionInputPart.inputText()).contains(inputText) + assertThat(decisionQuestionInputPart.inputImage()).isEmpty + assertThat(decisionQuestionInputPart.inputJson()).isEmpty + } + + @Test + fun ofInputTextRoundtrip() { + val jsonMapper = jsonMapper() + val decisionQuestionInputPart = + DecisionQuestionInputPart.ofInputText( + DecisionQuestionInputText.builder().text("text").build() + ) + + val roundtrippedDecisionQuestionInputPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionQuestionInputPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionQuestionInputPart).isEqualTo(decisionQuestionInputPart) + } + + @Test + fun ofInputImage() { + val inputImage = + DecisionQuestionInputImage.builder() + .imageUrl("https://") + .detail(DecisionQuestionInputImage.Detail.LOW) + .build() + + val decisionQuestionInputPart = DecisionQuestionInputPart.ofInputImage(inputImage) + + assertThat(decisionQuestionInputPart.inputText()).isEmpty + assertThat(decisionQuestionInputPart.inputImage()).contains(inputImage) + assertThat(decisionQuestionInputPart.inputJson()).isEmpty + } + + @Test + fun ofInputImageRoundtrip() { + val jsonMapper = jsonMapper() + val decisionQuestionInputPart = + DecisionQuestionInputPart.ofInputImage( + DecisionQuestionInputImage.builder() + .imageUrl("https://") + .detail(DecisionQuestionInputImage.Detail.LOW) + .build() + ) + + val roundtrippedDecisionQuestionInputPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionQuestionInputPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionQuestionInputPart).isEqualTo(decisionQuestionInputPart) + } + + @Test + fun ofInputJson() { + val inputJson = + DecisionInputJson.builder() + .data(DecisionInputJson.Data.UnionMember0.builder().build()) + .build() + + val decisionQuestionInputPart = DecisionQuestionInputPart.ofInputJson(inputJson) + + assertThat(decisionQuestionInputPart.inputText()).isEmpty + assertThat(decisionQuestionInputPart.inputImage()).isEmpty + assertThat(decisionQuestionInputPart.inputJson()).contains(inputJson) + } + + @Test + fun ofInputJsonRoundtrip() { + val jsonMapper = jsonMapper() + val decisionQuestionInputPart = + DecisionQuestionInputPart.ofInputJson( + DecisionInputJson.builder() + .data(DecisionInputJson.Data.UnionMember0.builder().build()) + .build() + ) + + val roundtrippedDecisionQuestionInputPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionQuestionInputPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionQuestionInputPart).isEqualTo(decisionQuestionInputPart) + } + + 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 decisionQuestionInputPart = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { decisionQuestionInputPart.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputTextTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputTextTest.kt new file mode 100644 index 000000000..096c7570c --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputTextTest.kt @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +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 DecisionQuestionInputTextTest { + + @Test + fun createAndRoundtrip() { + val decisionQuestionInputText = DecisionQuestionInputText.builder().text("text").build() + + assertThat(decisionQuestionInputText.text()).isEqualTo("text") + + val jsonMapper = jsonMapper() + + val roundtrippedDecisionQuestionInputText = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionQuestionInputText), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionQuestionInputText).isEqualTo(decisionQuestionInputText) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionCreateParamsTest.kt index 04014f4c9..7fa772d14 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionCreateParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionCreateParamsTest.kt @@ -14,7 +14,7 @@ internal class DecisionCreateParamsTest { .model("model") .addQuestion( DecisionCreateParams.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -37,7 +37,7 @@ internal class DecisionCreateParamsTest { .model("model") .addQuestion( DecisionCreateParams.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -59,7 +59,7 @@ internal class DecisionCreateParamsTest { .containsExactly( DecisionCreateParams.Question.ofPredicate( DecisionCreateParams.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -81,7 +81,7 @@ internal class DecisionCreateParamsTest { DecisionCreateParams.builder() .input("string") .model("model") - .addPredicateQuestion("instructions") + .addPredicateQuestion("string") .build() val body = params._body() @@ -91,9 +91,7 @@ internal class DecisionCreateParamsTest { assertThat(body.questions()) .containsExactly( DecisionCreateParams.Question.ofPredicate( - DecisionCreateParams.Question.Predicate.builder() - .instructions("instructions") - .build() + DecisionCreateParams.Question.Predicate.builder().instructions("string").build() ) ) } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputItemTest.kt new file mode 100644 index 000000000..de8a89504 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputItemTest.kt @@ -0,0 +1,202 @@ +// File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +package com.openai.models.decisions + +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 DecisionInputItemTest { + + @Test + fun ofFunctionCall() { + val functionCall = + DecisionInputItem.FunctionCall.builder() + .arguments("arguments") + .callId("x") + .name("name") + .id("id") + .namespace("namespace") + .status(DecisionInputItem.FunctionCall.Status.IN_PROGRESS) + .build() + + val decisionInputItem = DecisionInputItem.ofFunctionCall(functionCall) + + assertThat(decisionInputItem.functionCall()).contains(functionCall) + assertThat(decisionInputItem.functionCallOutput()).isEmpty + assertThat(decisionInputItem.customToolCall()).isEmpty + assertThat(decisionInputItem.customToolCallOutput()).isEmpty + } + + @Test + fun ofFunctionCallRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputItem = + DecisionInputItem.ofFunctionCall( + DecisionInputItem.FunctionCall.builder() + .arguments("arguments") + .callId("x") + .name("name") + .id("id") + .namespace("namespace") + .status(DecisionInputItem.FunctionCall.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedDecisionInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputItem).isEqualTo(decisionInputItem) + } + + @Test + fun ofFunctionCallOutput() { + val functionCallOutput = + DecisionInputItem.FunctionCallOutput.builder() + .callId("x") + .output("string") + .id("id") + .status(DecisionInputItem.FunctionCallOutput.Status.IN_PROGRESS) + .build() + + val decisionInputItem = DecisionInputItem.ofFunctionCallOutput(functionCallOutput) + + assertThat(decisionInputItem.functionCall()).isEmpty + assertThat(decisionInputItem.functionCallOutput()).contains(functionCallOutput) + assertThat(decisionInputItem.customToolCall()).isEmpty + assertThat(decisionInputItem.customToolCallOutput()).isEmpty + } + + @Test + fun ofFunctionCallOutputRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputItem = + DecisionInputItem.ofFunctionCallOutput( + DecisionInputItem.FunctionCallOutput.builder() + .callId("x") + .output("string") + .id("id") + .status(DecisionInputItem.FunctionCallOutput.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedDecisionInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputItem).isEqualTo(decisionInputItem) + } + + @Test + fun ofCustomToolCall() { + val customToolCall = + DecisionInputItem.CustomToolCall.builder() + .callId("x") + .input("input") + .name("name") + .id("id") + .namespace("namespace") + .status(DecisionInputItem.CustomToolCall.Status.IN_PROGRESS) + .build() + + val decisionInputItem = DecisionInputItem.ofCustomToolCall(customToolCall) + + assertThat(decisionInputItem.functionCall()).isEmpty + assertThat(decisionInputItem.functionCallOutput()).isEmpty + assertThat(decisionInputItem.customToolCall()).contains(customToolCall) + assertThat(decisionInputItem.customToolCallOutput()).isEmpty + } + + @Test + fun ofCustomToolCallRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputItem = + DecisionInputItem.ofCustomToolCall( + DecisionInputItem.CustomToolCall.builder() + .callId("x") + .input("input") + .name("name") + .id("id") + .namespace("namespace") + .status(DecisionInputItem.CustomToolCall.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedDecisionInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputItem).isEqualTo(decisionInputItem) + } + + @Test + fun ofCustomToolCallOutput() { + val customToolCallOutput = + DecisionInputItem.CustomToolCallOutput.builder() + .callId("x") + .output("string") + .id("id") + .status(DecisionInputItem.CustomToolCallOutput.Status.IN_PROGRESS) + .build() + + val decisionInputItem = DecisionInputItem.ofCustomToolCallOutput(customToolCallOutput) + + assertThat(decisionInputItem.functionCall()).isEmpty + assertThat(decisionInputItem.functionCallOutput()).isEmpty + assertThat(decisionInputItem.customToolCall()).isEmpty + assertThat(decisionInputItem.customToolCallOutput()).contains(customToolCallOutput) + } + + @Test + fun ofCustomToolCallOutputRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputItem = + DecisionInputItem.ofCustomToolCallOutput( + DecisionInputItem.CustomToolCallOutput.builder() + .callId("x") + .output("string") + .id("id") + .status(DecisionInputItem.CustomToolCallOutput.Status.IN_PROGRESS) + .build() + ) + + val roundtrippedDecisionInputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputItem).isEqualTo(decisionInputItem) + } + + 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 decisionInputItem = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { decisionInputItem.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputMessageTest.kt index bfc5426cf..0cbf21005 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputMessageTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputMessageTest.kt @@ -3,25 +3,79 @@ package com.openai.models.decisions 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 DecisionInputMessageTest { @Test - fun createAndRoundtrip() { + fun ofUser() { + val user = + DecisionInputMessage.User.builder() + .content("string") + .type(DecisionInputMessage.User.Type.MESSAGE) + .build() + + val decisionInputMessage = DecisionInputMessage.ofUser(user) + + assertThat(decisionInputMessage.user()).contains(user) + assertThat(decisionInputMessage.developer()).isEmpty + assertThat(decisionInputMessage.system()).isEmpty + assertThat(decisionInputMessage.assistant()).isEmpty + } + + @Test + fun ofUserRoundtrip() { + val jsonMapper = jsonMapper() val decisionInputMessage = - DecisionInputMessage.builder() + DecisionInputMessage.ofUser( + DecisionInputMessage.User.builder() + .content("string") + .type(DecisionInputMessage.User.Type.MESSAGE) + .build() + ) + + val roundtrippedDecisionInputMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputMessage).isEqualTo(decisionInputMessage) + } + + @Test + fun ofDeveloper() { + val developer = + DecisionInputMessage.Developer.builder() .content("string") - .type(DecisionInputMessage.Type.MESSAGE) + .type(DecisionInputMessage.Developer.Type.MESSAGE) .build() - assertThat(decisionInputMessage.content()) - .isEqualTo(DecisionInputMessage.Content.ofString("string")) - assertThat(decisionInputMessage.type()).contains(DecisionInputMessage.Type.MESSAGE) + val decisionInputMessage = DecisionInputMessage.ofDeveloper(developer) + + assertThat(decisionInputMessage.user()).isEmpty + assertThat(decisionInputMessage.developer()).contains(developer) + assertThat(decisionInputMessage.system()).isEmpty + assertThat(decisionInputMessage.assistant()).isEmpty + } + @Test + fun ofDeveloperRoundtrip() { val jsonMapper = jsonMapper() + val decisionInputMessage = + DecisionInputMessage.ofDeveloper( + DecisionInputMessage.Developer.builder() + .content("string") + .type(DecisionInputMessage.Developer.Type.MESSAGE) + .build() + ) val roundtrippedDecisionInputMessage = jsonMapper.readValue( @@ -31,4 +85,96 @@ internal class DecisionInputMessageTest { assertThat(roundtrippedDecisionInputMessage).isEqualTo(decisionInputMessage) } + + @Test + fun ofSystem() { + val system = + DecisionInputMessage.System.builder() + .content("string") + .type(DecisionInputMessage.System.Type.MESSAGE) + .build() + + val decisionInputMessage = DecisionInputMessage.ofSystem(system) + + assertThat(decisionInputMessage.user()).isEmpty + assertThat(decisionInputMessage.developer()).isEmpty + assertThat(decisionInputMessage.system()).contains(system) + assertThat(decisionInputMessage.assistant()).isEmpty + } + + @Test + fun ofSystemRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputMessage = + DecisionInputMessage.ofSystem( + DecisionInputMessage.System.builder() + .content("string") + .type(DecisionInputMessage.System.Type.MESSAGE) + .build() + ) + + val roundtrippedDecisionInputMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputMessage).isEqualTo(decisionInputMessage) + } + + @Test + fun ofAssistant() { + val assistant = + DecisionInputMessage.Assistant.builder() + .content("string") + .phase(DecisionInputMessage.Assistant.Phase.COMMENTARY) + .type(DecisionInputMessage.Assistant.Type.MESSAGE) + .build() + + val decisionInputMessage = DecisionInputMessage.ofAssistant(assistant) + + assertThat(decisionInputMessage.user()).isEmpty + assertThat(decisionInputMessage.developer()).isEmpty + assertThat(decisionInputMessage.system()).isEmpty + assertThat(decisionInputMessage.assistant()).contains(assistant) + } + + @Test + fun ofAssistantRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputMessage = + DecisionInputMessage.ofAssistant( + DecisionInputMessage.Assistant.builder() + .content("string") + .phase(DecisionInputMessage.Assistant.Phase.COMMENTARY) + .type(DecisionInputMessage.Assistant.Type.MESSAGE) + .build() + ) + + val roundtrippedDecisionInputMessage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputMessage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputMessage).isEqualTo(decisionInputMessage) + } + + 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 decisionInputMessage = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { decisionInputMessage.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputPartTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputPartTest.kt index 116a11406..50dac1c2c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputPartTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputPartTest.kt @@ -6,6 +6,7 @@ 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.DecisionInputJson import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -26,6 +27,7 @@ internal class DecisionInputPartTest { assertThat(decisionInputPart.inputText()).contains(inputText) assertThat(decisionInputPart.inputImage()).isEmpty + assertThat(decisionInputPart.inputJson()).isEmpty } @Test @@ -61,6 +63,7 @@ internal class DecisionInputPartTest { assertThat(decisionInputPart.inputText()).isEmpty assertThat(decisionInputPart.inputImage()).contains(inputImage) + assertThat(decisionInputPart.inputJson()).isEmpty } @Test @@ -84,6 +87,39 @@ internal class DecisionInputPartTest { assertThat(roundtrippedDecisionInputPart).isEqualTo(decisionInputPart) } + @Test + fun ofInputJson() { + val inputJson = + DecisionInputJson.builder() + .data(DecisionInputJson.Data.UnionMember0.builder().build()) + .build() + + val decisionInputPart = DecisionInputPart.ofInputJson(inputJson) + + assertThat(decisionInputPart.inputText()).isEmpty + assertThat(decisionInputPart.inputImage()).isEmpty + assertThat(decisionInputPart.inputJson()).contains(inputJson) + } + + @Test + fun ofInputJsonRoundtrip() { + val jsonMapper = jsonMapper() + val decisionInputPart = + DecisionInputPart.ofInputJson( + DecisionInputJson.builder() + .data(DecisionInputJson.Data.UnionMember0.builder().build()) + .build() + ) + + val roundtrippedDecisionInputPart = + jsonMapper.readValue( + jsonMapper.writeValueAsString(decisionInputPart), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDecisionInputPart).isEqualTo(decisionInputPart) + } + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { BOOLEAN(JsonValue.from(false)), STRING(JsonValue.from("invalid")), diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/ClientEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/ClientEventTest.kt index 2bd59d81b..da2b261a6 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/ClientEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/ClientEventTest.kt @@ -66,7 +66,7 @@ internal class ClientEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -153,7 +153,7 @@ internal class ClientEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -249,7 +249,7 @@ internal class ClientEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -357,7 +357,7 @@ internal class ClientEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionTriggerTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionTriggerTest.kt index 6ec597894..a4b70b6c4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionTriggerTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionTriggerTest.kt @@ -17,7 +17,7 @@ internal class DecisionTriggerTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -31,7 +31,7 @@ internal class DecisionTriggerTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationConfigTest.kt index 1552fa514..9b8cd9a2a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationConfigTest.kt @@ -16,7 +16,7 @@ internal class DecisionsDelegationConfigTest { .model("x") .addQuestion( DecisionsDelegationConfig.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -27,7 +27,7 @@ internal class DecisionsDelegationConfigTest { .containsExactly( DecisionsDelegationConfig.Question.ofPredicate( DecisionsDelegationConfig.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfigTest.kt index eeeb02765..3180673a9 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfigTest.kt @@ -17,7 +17,7 @@ internal class DecisionsDelegationUpdateConfigTest { .model("x") .addQuestion( DecisionsDelegationUpdateConfig.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -28,7 +28,7 @@ internal class DecisionsDelegationUpdateConfigTest { .containsExactly( DecisionsDelegationUpdateConfig.Question.ofPredicate( DecisionsDelegationUpdateConfig.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/LiveCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/LiveCreateParamsTest.kt index d95c2e30f..d5afa696c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/LiveCreateParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/LiveCreateParamsTest.kt @@ -54,7 +54,7 @@ internal class LiveCreateParamsTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -117,7 +117,7 @@ internal class LiveCreateParamsTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -178,7 +178,7 @@ internal class LiveCreateParamsTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/MediaSessionConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/MediaSessionConfigTest.kt index f5238178b..90ce9848b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/MediaSessionConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/MediaSessionConfigTest.kt @@ -56,7 +56,7 @@ internal class MediaSessionConfigTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -114,7 +114,7 @@ internal class MediaSessionConfigTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/ServerEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/ServerEventTest.kt index 006af88c1..1b6be28b7 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/ServerEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/ServerEventTest.kt @@ -57,7 +57,7 @@ internal class ServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -147,7 +147,7 @@ internal class ServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -242,7 +242,7 @@ internal class ServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -359,7 +359,7 @@ internal class ServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -1601,7 +1601,7 @@ internal class ServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -1693,7 +1693,7 @@ internal class ServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionClosedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionClosedEventTest.kt index e1da55898..636aeff18 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionClosedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionClosedEventTest.kt @@ -52,7 +52,7 @@ internal class SessionClosedEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -107,7 +107,7 @@ internal class SessionClosedEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionConfigTest.kt index d26cb12bb..4d734ca1a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionConfigTest.kt @@ -55,7 +55,7 @@ internal class SessionConfigTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -112,7 +112,7 @@ internal class SessionConfigTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionResourceTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionResourceTest.kt index 3e926a13e..c89f5369f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionResourceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionResourceTest.kt @@ -57,7 +57,7 @@ internal class SessionResourceTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -118,7 +118,7 @@ internal class SessionResourceTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartEventTest.kt index f9f653c22..7f5f10a1b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartEventTest.kt @@ -60,7 +60,7 @@ internal class SessionStartEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -122,7 +122,7 @@ internal class SessionStartEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartedEventTest.kt index 97c138790..9e9e79424 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartedEventTest.kt @@ -51,7 +51,7 @@ internal class SessionStartedEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -104,7 +104,7 @@ internal class SessionStartedEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateConfigTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateConfigTest.kt index a42cb9a6e..5f588e1ec 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateConfigTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateConfigTest.kt @@ -21,7 +21,7 @@ internal class SessionUpdateConfigTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -41,7 +41,7 @@ internal class SessionUpdateConfigTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateEventTest.kt index 0c3f1f17f..aac9283d3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateEventTest.kt @@ -80,7 +80,7 @@ internal class SessionUpdateEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -157,7 +157,7 @@ internal class SessionUpdateEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdatedEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdatedEventTest.kt index 1c6983ffa..eaa4edc7c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdatedEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdatedEventTest.kt @@ -78,7 +78,7 @@ internal class SessionUpdatedEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -156,7 +156,7 @@ internal class SessionUpdatedEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkClientEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkClientEventTest.kt index 34c34ea49..2dcfabcda 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkClientEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkClientEventTest.kt @@ -307,7 +307,7 @@ internal class ForkClientEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -415,7 +415,7 @@ internal class ForkClientEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkServerEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkServerEventTest.kt index d5059cc79..824f12c81 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkServerEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkServerEventTest.kt @@ -88,7 +88,7 @@ internal class ForkServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -178,7 +178,7 @@ internal class ForkServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -273,7 +273,7 @@ internal class ForkServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -390,7 +390,7 @@ internal class ForkServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -1637,7 +1637,7 @@ internal class ForkServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -1729,7 +1729,7 @@ internal class ForkServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/sessions/SessionAcceptParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/sessions/SessionAcceptParamsTest.kt index e9e60d870..88ee17985 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/sessions/SessionAcceptParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/sessions/SessionAcceptParamsTest.kt @@ -49,7 +49,7 @@ internal class SessionAcceptParamsTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -119,7 +119,7 @@ internal class SessionAcceptParamsTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -169,7 +169,7 @@ internal class SessionAcceptParamsTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandClientEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandClientEventTest.kt index d3087cccd..d055edf91 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandClientEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandClientEventTest.kt @@ -102,7 +102,7 @@ internal class SidebandClientEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -208,7 +208,7 @@ internal class SidebandClientEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandServerEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandServerEventTest.kt index 84819ec88..8fe9414bb 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandServerEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandServerEventTest.kt @@ -88,7 +88,7 @@ internal class SidebandServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -178,7 +178,7 @@ internal class SidebandServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -273,7 +273,7 @@ internal class SidebandServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -390,7 +390,7 @@ internal class SidebandServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -1531,7 +1531,7 @@ internal class SidebandServerEventTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) @@ -1623,7 +1623,7 @@ internal class SidebandServerEventTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/DecisionServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/DecisionServiceAsyncTest.kt index c351ed9d9..998dbb0f3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/DecisionServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/DecisionServiceAsyncTest.kt @@ -28,7 +28,7 @@ internal class DecisionServiceAsyncTest { .model("model") .addQuestion( DecisionCreateParams.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/LiveServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/LiveServiceAsyncTest.kt index 9ecc6cb44..5b1704797 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/LiveServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/LiveServiceAsyncTest.kt @@ -76,7 +76,7 @@ internal class LiveServiceAsyncTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/live/SessionServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/live/SessionServiceAsyncTest.kt index 73899a4ca..10300ae4e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/live/SessionServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/live/SessionServiceAsyncTest.kt @@ -83,7 +83,7 @@ internal class SessionServiceAsyncTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/DecisionServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/DecisionServiceTest.kt index 1cbac7b77..66581ead3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/DecisionServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/DecisionServiceTest.kt @@ -28,7 +28,7 @@ internal class DecisionServiceTest { .model("model") .addQuestion( DecisionCreateParams.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/LiveServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/LiveServiceTest.kt index 870e1f3ed..24911cea7 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/LiveServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/LiveServiceTest.kt @@ -76,7 +76,7 @@ internal class LiveServiceTest { .addQuestion( DecisionTrigger.Decisions.Question.Predicate .builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/live/SessionServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/live/SessionServiceTest.kt index fb9347e1a..e13a267c5 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/live/SessionServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/live/SessionServiceTest.kt @@ -81,7 +81,7 @@ internal class SessionServiceTest { DecisionTrigger.Decisions.builder() .addQuestion( DecisionTrigger.Decisions.Question.Predicate.builder() - .instructions("instructions") + .instructions("string") .name("name") .build() ) diff --git a/scripts/castiron/CUSTOM_CODE.md b/scripts/castiron/CUSTOM_CODE.md index 945491bb4..2b5fcc967 100644 --- a/scripts/castiron/CUSTOM_CODE.md +++ b/scripts/castiron/CUSTOM_CODE.md @@ -122,6 +122,11 @@ Next uses separate `Castiron next / budget-only change` and two tracks cannot overwrite each other's authorization. The policy version is fixed by next's protected base; advancing main does not silently change it. Changing the reviewed pin changes next's own base and rebuilds its merge queue. +Review invalidation reads the effective checker's inert `PIN_REVIEW_POLICY` +capability marker at that exact revision. Legacy or unknown versions retain +human review for every pin edit; only the recognized effective-policy contract +allows non-increasing forward pin changes to use ordinary review. No checker +code is executed by the invalidation handler. The effective policy is read from the selected authenticated main commit. The next base selects that version; a candidate never selects its own executing diff --git a/scripts/castiron/custom_code_policy.py b/scripts/castiron/custom_code_policy.py index a8410cb94..5631fc5bf 100755 --- a/scripts/castiron/custom_code_policy.py +++ b/scripts/castiron/custom_code_policy.py @@ -30,6 +30,8 @@ POLICY = ".castiron-ratchet.json" NEXT_POLICY_VERSION = 1 +# Inert capability marker read by trusted review invalidation at this exact revision. +PIN_REVIEW_POLICY = "effective-policy-v1" SELECTION_RECEIPT_VERSION = 1 diff --git a/scripts/castiron/fixtures/github_publisher.cjs b/scripts/castiron/fixtures/github_publisher.cjs index a2d0e4722..69664de46 100644 --- a/scripts/castiron/fixtures/github_publisher.cjs +++ b/scripts/castiron/fixtures/github_publisher.cjs @@ -8,7 +8,9 @@ const github = { actions: { getWorkflowRun: async ({ run_id }) => { if (data.run_errors?.[run_id]) - throw Object.assign(new Error('Workflow run lookup failed'), { status: data.run_errors[run_id] }); + throw Object.assign(new Error('Workflow run lookup failed'), { + status: data.run_errors[run_id], + }); return { data: data.runs_by_id?.[run_id] || data.run }; }, getWorkflow: async ({ workflow_id }) => ({ @@ -47,12 +49,25 @@ const github = { repos: { getCommit: async () => ({ data: data.merge_commit }), compareCommitsWithBasehead: async ({ basehead }) => ({ - data: data.comparisons?.[basehead] || { merge_base_commit: { sha: basehead.split('...')[0] } }, + data: data.comparisons?.[basehead] || { + merge_base_commit: { sha: basehead.split('...')[0] }, + }, }), getContent: async ({ path, ref }) => { - if (path !== '.castiron-ratchet.json' || !data.policies_by_sha?.[ref]) + let source; + if (path === 'scripts/castiron/custom_code_policy.py') { + if (data.checker_lookup_error) + throw Object.assign(new Error('Checker lookup failed'), { + status: data.checker_lookup_error, + }); + source = data.checker_sources_by_sha?.[ref]; + if (typeof source !== 'string') throw Object.assign(new Error('Missing checker'), { status: 404 }); + } else if (path === '.castiron-ratchet.json' && data.policies_by_sha?.[ref]) { + source = JSON.stringify(data.policies_by_sha[ref]); + } else { throw new Error('Unexpected policy lookup'); - const content = Buffer.from(JSON.stringify(data.policies_by_sha[ref])); + } + const content = Buffer.from(source); return { data: { type: 'file', diff --git a/scripts/castiron/generated-files.json b/scripts/castiron/generated-files.json index d67d4bfd2..2f15e39b7 100644 --- a/scripts/castiron/generated-files.json +++ b/scripts/castiron/generated-files.json @@ -3,7 +3,7 @@ "files": { ".github/workflows/castiron-custom-code-comment.yml": { "mode": "100644", - "sha256": "a896f4bac69c6f8debe7b4bf540129466be04cb62b75c60f5f80a37f5739b0a5" + "sha256": "87fdffd260f458095f3a0e13e1290a80d9ce484e516092b7fbf5f34fb3847fb3" }, ".github/workflows/castiron-custom-code.yml": { "mode": "100644", @@ -57,6 +57,22 @@ "mode": "100644", "sha256": "e0b996995abf2261a3d39ea7c03b78b0614e3470750efe4e2ec4bb7a7d00fb6c" }, + "openai-java-core/src/main/kotlin/com/openai/models/DecisionInputJson.kt": { + "mode": "100644", + "sha256": "b8b1b7cebf674b32a6573f53ce2454f9bd8614fccc74aa61fc1ab04cd7d86efb" + }, + "openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputImage.kt": { + "mode": "100644", + "sha256": "7bbcb3d4c9869c5155c944feeff1e9f6439866fe1fc9c9df7990c903eac08dc6" + }, + "openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputPart.kt": { + "mode": "100644", + "sha256": "cc2abf521a79fd41fb6332a0db9a93b51fa03b428ceec26590b576d6e342b2dd" + }, + "openai-java-core/src/main/kotlin/com/openai/models/DecisionQuestionInputText.kt": { + "mode": "100644", + "sha256": "6724970059f343fb80b51bb8e614ed98988a3506af4f5f6fac6b149feca00a8c" + }, "openai-java-core/src/main/kotlin/com/openai/models/ErrorObject.kt": { "mode": "100644", "sha256": "01b4f442fca9dc96a2fb09c947cc590a85a62652d3029914637d1b5e7f7a1993" @@ -3971,19 +3987,23 @@ }, "openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionCreateParams.kt": { "mode": "100644", - "sha256": "7003331e3ff78e1d1a5338482efb5a5419259d16dc165e9558beea68c885f06f" + "sha256": "744045dcb0437c576598992c5d87bfe43c2f97e699c538f8a34b78f73e0cbbb6" }, "openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputImage.kt": { "mode": "100644", "sha256": "903b79600bcd4027da1ce281b798dd443ec1c53648fd938dd38b17d0344c727e" }, + "openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputItem.kt": { + "mode": "100644", + "sha256": "10735a435005e561acf1d790e8cda34b67a8b6bfcf9a59dec3478940b45c8ed6" + }, "openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputMessage.kt": { "mode": "100644", - "sha256": "21bb3d12951990b64247599c873c8c2f4a71769cc9d2c4faff853564b34a6476" + "sha256": "e2b7850d0c567242e57aee02571c80f1657e745b5512fca47c0542e515ff48ef" }, "openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputPart.kt": { "mode": "100644", - "sha256": "6f9fccc18efe1c6bb68c7de5419d1c2c854889c5255bf4f963ff1eec4f998ba6" + "sha256": "c2827a8c7b135dcb17529139abdeee3a93b82a72a8508e9f946fa2c3044df9ea" }, "openai-java-core/src/main/kotlin/com/openai/models/decisions/DecisionInputText.kt": { "mode": "100644", @@ -4527,15 +4547,15 @@ }, "openai-java-core/src/main/kotlin/com/openai/models/live/DecisionTrigger.kt": { "mode": "100644", - "sha256": "2383414a253ae32eb3a2c94a982d686491ea01d589c5b5e629fac0766a0c98ca" + "sha256": "20c56c688355b0002125c52edde0a115f9f885e676c9a6cf12449acb6999456f" }, "openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationConfig.kt": { "mode": "100644", - "sha256": "b93a28592cab45ad0ca5dc53e5a1b48cc61a7a6a0d47f4d35c773b299c55e0a9" + "sha256": "6a67cc903125f280b945dbcf62329701ef1ae8f27488589610a5adb5035e09ac" }, "openai-java-core/src/main/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfig.kt": { "mode": "100644", - "sha256": "473f654e9a7158db3b70d4220db1c4697b24a16782ca89111e67d878ca0242fa" + "sha256": "95261e7fb2540c5373d048ab2968d3bc7c329f19da864dbdc8682ecd4061bc9d" }, "openai-java-core/src/main/kotlin/com/openai/models/live/DelegationCreatedEvent.kt": { "mode": "100644", @@ -8657,6 +8677,22 @@ "mode": "100644", "sha256": "9fe1ef434bcb3bb2110eb85e0b5f5bc607c35b59a209c33c003b422b91179a68" }, + "openai-java-core/src/test/kotlin/com/openai/models/DecisionInputJsonTest.kt": { + "mode": "100644", + "sha256": "83d42b4daea1a418f489c898f84175a4c2162743349c0362e2b09d83f42158ed" + }, + "openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputImageTest.kt": { + "mode": "100644", + "sha256": "b3e7580b1d47651b07f8455716935ce41e501dbff2b1584a8148e5de12bc1feb" + }, + "openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputPartTest.kt": { + "mode": "100644", + "sha256": "568db5d64d108ce0ba19cc5f99f2856bdcc8d8bd3e740e519d1be951e0f0126e" + }, + "openai-java-core/src/test/kotlin/com/openai/models/DecisionQuestionInputTextTest.kt": { + "mode": "100644", + "sha256": "f4d339d28f86bb1ba2be2d73eea6af2fc7712e478532e071f382924d49e3ab44" + }, "openai-java-core/src/test/kotlin/com/openai/models/ErrorObjectTest.kt": { "mode": "100644", "sha256": "a29b56c44f00f85e8c9b4ee015b1c02b423233e102a9881c2c9d046773a83ce4" @@ -12019,19 +12055,23 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionCreateParamsTest.kt": { "mode": "100644", - "sha256": "f5d23f953739ec121a9dfdbabb0f7e28e30b59650776b2324ca1bf5249ee4643" + "sha256": "00f4ab806f1c1d61fa9570761144ae3ba73b58dd5a0096fde553d4a22bc1bdc8" }, "openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputImageTest.kt": { "mode": "100644", "sha256": "3dd9f8965f7ed1678d5040d6e1ef235d7f21ddecb476f1c530daad8ad936f7b6" }, + "openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputItemTest.kt": { + "mode": "100644", + "sha256": "ae7be09f2135aaa78c11e34fdfaa5930c704aad2acfd9f2e143252849e55b3c6" + }, "openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputMessageTest.kt": { "mode": "100644", - "sha256": "33928e65ee5555270110107c0f174db09fadeb2f0ca19d2caa729999e1af1e41" + "sha256": "a0a0b33456fb88cf98fbe1f014c734d45009be91a422b6d6d65177144be9d84d" }, "openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputPartTest.kt": { "mode": "100644", - "sha256": "0aab2cb9eaf6d3ed8a3ef47ab8438602d653c7ac70c553e401889d86e7d2c806" + "sha256": "7e8618a626ca60cd82cae80adfc9abeb25c1552941cc1d7b94a8c780537d7a8c" }, "openai-java-core/src/test/kotlin/com/openai/models/decisions/DecisionInputTextTest.kt": { "mode": "100644", @@ -12447,7 +12487,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/ClientEventTest.kt": { "mode": "100644", - "sha256": "863d227fb4ab99aa3155b2e439897da099dfab027f15ca17afb6c5d7767e13ad" + "sha256": "5e3beb4424dea99afda7b8d2480527d355006defba3232a5b7e0af2d1250e973" }, "openai-java-core/src/test/kotlin/com/openai/models/live/CommentaryAppendEventTest.kt": { "mode": "100644", @@ -12491,15 +12531,15 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/DecisionTriggerTest.kt": { "mode": "100644", - "sha256": "2751ebfae02a2e99b1c6681e11d55c81b3c50e6d73574fc284664efcdca65e7b" + "sha256": "bb1f75f8d0b28099c83cb637aa01e6c5cd9f8e266c09eb10589668e0f947a7d7" }, "openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationConfigTest.kt": { "mode": "100644", - "sha256": "d49d145d432616bc0af61faec01c79a087b93d7a13f40e538ad1fe00347dc2b7" + "sha256": "8367f251c58ca69b8c7cd49774b22e0a4324f6b70911514dcbc8a2db3942e443" }, "openai-java-core/src/test/kotlin/com/openai/models/live/DecisionsDelegationUpdateConfigTest.kt": { "mode": "100644", - "sha256": "928e81fe3c2f71123c4684e2c04ab0ff9a613120c851c99b8c95282279b60f7b" + "sha256": "61f697458467918907704ccc810cd37f912d32dab4dac2f760da5c6e9ed91fc9" }, "openai-java-core/src/test/kotlin/com/openai/models/live/DelegationCreatedEventTest.kt": { "mode": "100644", @@ -12567,7 +12607,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/LiveCreateParamsTest.kt": { "mode": "100644", - "sha256": "b087b903c0bf59a7b04005d828c24b232c52d41f45b7640cb586e5e199418b8b" + "sha256": "09922e622b1fde8489b0edd61187eb91a7b24d68b98cac7efe9a8aef2ba0ae74" }, "openai-java-core/src/test/kotlin/com/openai/models/live/LiveCreateResponseTest.kt": { "mode": "100644", @@ -12575,7 +12615,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/MediaSessionConfigTest.kt": { "mode": "100644", - "sha256": "c6b46e95dba25da69f034f5b77016160cd82f350ac36502aeb6bff9764624ddb" + "sha256": "17f29bef574c6144b634fca8d70978f8182b23198ecf958fce3a20b125287670" }, "openai-java-core/src/test/kotlin/com/openai/models/live/MediaSessionForkConfigTest.kt": { "mode": "100644", @@ -12627,7 +12667,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/ServerEventTest.kt": { "mode": "100644", - "sha256": "8c396b9212ea0657382b6e0fee5d5538d93fca37f20997b732943551d98e55db" + "sha256": "5cb5b46d322aafadacc6c19f8d21c05f65b5d60822b6727b46aeed918891ffe2" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionCloseEventTest.kt": { "mode": "100644", @@ -12635,35 +12675,35 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionClosedEventTest.kt": { "mode": "100644", - "sha256": "e7393c86d2b57abcfd5fe81f46b974157a5e774d41e6f6fdec9d36323c30629f" + "sha256": "d600097dc858aba3baef15cdf5ad65f75b41e155a37b0568c798c9745b660169" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionConfigTest.kt": { "mode": "100644", - "sha256": "9b8afa25b67c4a6524510de26bfbb716c2eb92f12cbf3a54143e23dcae8d5116" + "sha256": "0c0d46668eff01df223e37206db02c035d69deb408ac334353dd8353a1ade374" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionResourceTest.kt": { "mode": "100644", - "sha256": "42a96f10dc1699e0193236853db4f8043e8d3c106cefc589146659b45dd57e7b" + "sha256": "abeac9560e41d59677f81aeacdacd3683a5d22fdae311b3329334e4f7e11f0b8" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartEventTest.kt": { "mode": "100644", - "sha256": "b96ea53a654fc052bf5e6bf770238d5e86b0e02b7b82d10a98db8996b510ba0e" + "sha256": "cc1e9d1ab53ceedf0a48789e27a5e679a5cf922084b5e29ee9d7c61ff9ae2918" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionStartedEventTest.kt": { "mode": "100644", - "sha256": "2cab6fffe37aa5267f2d53d00d437d695aa4610c29ebd6b09a8c89e325ff7e15" + "sha256": "7e156904c9ce231ab5889170db7ef1e0f7c70dbe78cad1d1a937a2805b35d8f2" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateConfigTest.kt": { "mode": "100644", - "sha256": "70e69f323b6742aa661dbeaf6a4064c7c949f6e08e134525232f0deede5c9a06" + "sha256": "10e48964a3a123a1dd6e9ec75b8873487eb54fcd15ef8ad3dc48cece63955e5c" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdateEventTest.kt": { "mode": "100644", - "sha256": "513e6c8492714628779c9093d1fdbb78df2aa2b98bf8ffb3abe8822b76f31753" + "sha256": "aa6511c1db945ea316ca15b841f0860cd09c3859d0b686abd78e837b08ddc08c" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionUpdatedEventTest.kt": { "mode": "100644", - "sha256": "c6693c62e912f357dd65e7ff18375a0d32f3d11bec0b6642feca5b1abf2ad605" + "sha256": "366208110d2de9069385823d2a61b29e79724d417a7834f0a146493ab3ed0f11" }, "openai-java-core/src/test/kotlin/com/openai/models/live/SessionUsageTest.kt": { "mode": "100644", @@ -12683,15 +12723,15 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkClientEventTest.kt": { "mode": "100644", - "sha256": "ef738b096637124cac233b862b08eeb6fccdeb9d35344b80df3327e3c4319791" + "sha256": "acb95174048e322dabf75074c3cdd9071a48134d0cbea59efe31603e408807d6" }, "openai-java-core/src/test/kotlin/com/openai/models/live/forks/ForkServerEventTest.kt": { "mode": "100644", - "sha256": "0e1dbd04101a263ed512db66c62c5360f9960ecc470ebcdd239c2ff3b0eed567" + "sha256": "79c2d43a1d2a23746c53810c0baa6b128529b392ece51639b2125c2cba7a6302" }, "openai-java-core/src/test/kotlin/com/openai/models/live/sessions/SessionAcceptParamsTest.kt": { "mode": "100644", - "sha256": "150aaac81297ce3139f44544c56ee899b88eb066d3398f38a58ea25ed4bbf362" + "sha256": "a4cc5c49af7ce30359038e57fd52eefac47d9598562cf86def963bb773ada3f5" }, "openai-java-core/src/test/kotlin/com/openai/models/live/sessions/SessionDownloadRecordingParamsTest.kt": { "mode": "100644", @@ -12719,11 +12759,11 @@ }, "openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandClientEventTest.kt": { "mode": "100644", - "sha256": "ad56c4ffcb38c0fa39716d61d48cde8cb3d42301ef52b16b3b3ae0bd3a1dfb15" + "sha256": "c810f8438fbd22fa1a4ae134b0db01f2a584ef1e3d52e094b4737db29949ef64" }, "openai-java-core/src/test/kotlin/com/openai/models/live/sideband/SidebandServerEventTest.kt": { "mode": "100644", - "sha256": "fad09f50d5248b9fc0728cf1a6d9906f0c53c1bdd048e29e2d840e2948f6e34a" + "sha256": "8a80c488712d695d73cc7c6cd856e19690c572b901ee245c7e6bf3051edd541e" }, "openai-java-core/src/test/kotlin/com/openai/models/models/ModelDeleteParamsTest.kt": { "mode": "100644", @@ -14479,7 +14519,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/async/DecisionServiceAsyncTest.kt": { "mode": "100644", - "sha256": "5737c899e5070a895568588c5e611e38f58cdbc5d6ac7dc76f864b6ab3c91ac1" + "sha256": "5fded34c288c19cc0f66ccfd2139f4de761e3a2b244c42db94b4c3c9a705264b" }, "openai-java-core/src/test/kotlin/com/openai/services/async/EmbeddingServiceAsyncTest.kt": { "mode": "100644", @@ -14511,7 +14551,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/async/LiveServiceAsyncTest.kt": { "mode": "100644", - "sha256": "7f49924b25d327504d8393344aafa597c4ef89ac0baa627924a6d392ef8dbebb" + "sha256": "ea2fee3976f8cee30dc98d779cef79f5c562681209eba039907ec3c946dc462a" }, "openai-java-core/src/test/kotlin/com/openai/services/async/ModelServiceAsyncTest.kt": { "mode": "100644", @@ -15103,7 +15143,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/async/live/SessionServiceAsyncTest.kt": { "mode": "100644", - "sha256": "905e7acfd52b765294d4f0a3935546c29a4519e60ba11c5415f5aa8ce850f2d5" + "sha256": "94e322f45b797f037eaa24c1b3f2f49d596fd97cd56f7d7fa92162a608b37b3c" }, "openai-java-core/src/test/kotlin/com/openai/services/async/realtime/CallServiceAsyncTest.kt": { "mode": "100644", @@ -15211,7 +15251,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/DecisionServiceTest.kt": { "mode": "100644", - "sha256": "04714be876980e9f6003d646f3eea40e67264c8cb19ba40fa9e49b0813c8feb4" + "sha256": "9ffda2d094e559c71a9bd20db03d395e07ac30bd8646088d71383756cd368346" }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/EmbeddingServiceTest.kt": { "mode": "100644", @@ -15243,7 +15283,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/LiveServiceTest.kt": { "mode": "100644", - "sha256": "edc5dcf74e377af34f2ab078e1c0f9f27bc6588e9446cfb6481ae378b9bc72be" + "sha256": "6df9a025b883cff15cf3be005d7186e9b17a961ca3d073ac04baf33ff5338d11" }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/ModelServiceTest.kt": { "mode": "100644", @@ -15835,7 +15875,7 @@ }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/live/SessionServiceTest.kt": { "mode": "100644", - "sha256": "7012fb667391e1eb420b76171b16faf3d3fdaf9085b6e30c1b78ec5594836121" + "sha256": "fd5333ccc790d6597c2a0700a0c674fe745e5fc3c0a29cf2302f5d95dbe815d8" }, "openai-java-core/src/test/kotlin/com/openai/services/blocking/realtime/CallServiceTest.kt": { "mode": "100644", @@ -15911,7 +15951,7 @@ }, "scripts/castiron/CUSTOM_CODE.md": { "mode": "100644", - "sha256": "e7ac634501e8f21144cc5678539282b50b64830a0455b5b840f8ca4010033d3b" + "sha256": "ee7bd6b2e0da275985a3309393063ac1fa5946d024575d228ae4979ec03e6417" }, "scripts/castiron/README.md": { "mode": "100644", @@ -15923,7 +15963,7 @@ }, "scripts/castiron/custom_code_policy.py": { "mode": "100755", - "sha256": "c0c4d5191b90880c881675816ec18ea0bd4b078838ef663513159b2d157edb47" + "sha256": "8d7abe615f3cf8e43f1db7ed9674264f0e0fffea39b093a145731783a343a1d7" }, "scripts/castiron/custom_code_report.py": { "mode": "100755", @@ -15935,7 +15975,7 @@ }, "scripts/castiron/fixtures/github_publisher.cjs": { "mode": "100644", - "sha256": "7cd554d124da0ae9eb7fac3c4bbb1c8129f0d6868e442698105fe4945f6ee814" + "sha256": "232fd17ff25eb0680f71f82a04d210e9b2da4dd7fae6071058dc7108e3deecc6" }, "scripts/castiron/ruff.toml": { "mode": "100644", @@ -15955,7 +15995,7 @@ }, "scripts/castiron/test_custom_code_publication.py": { "mode": "100644", - "sha256": "8548acaf8958bf745759ba5c7a08791cb72793aa0a780d027a2a686e0464ac8e" + "sha256": "dc4492599c1c1b6cf9403b200ab105de10cc95ba91a45e566eb1bb673024d9fa" }, "scripts/castiron/test_custom_code_report.py": { "mode": "100644", diff --git a/scripts/castiron/test_custom_code_publication.py b/scripts/castiron/test_custom_code_publication.py index 1c225b6d2..15069d795 100644 --- a/scripts/castiron/test_custom_code_publication.py +++ b/scripts/castiron/test_custom_code_publication.py @@ -1473,6 +1473,7 @@ def pinned(checker: str) -> dict[str, Any]: target_checker: {"schema_version": 1, "max_custom_patch_lines": 5}, proposed_checker: {"schema_version": 1, "max_custom_patch_lines": 5}, } + payload["checker_sources_by_sha"] = {target_checker: Path(policy.__file__).read_text()} self.assertEqual(self.invoke(payload), []) payload["policies_by_sha"][proposed_checker]["max_custom_patch_lines"] = 7 self.assertEqual(len(self.invoke(payload)), 6) @@ -1482,6 +1483,69 @@ def pinned(checker: str) -> dict[str, Any]: } self.assertEqual(len(self.invoke(payload)), 6) + def test_legacy_checker_pin_reviews_still_invalidate_statuses(self) -> None: + previous, proposed = "e" * 40, "f" * 40 + for changed in ({"state": "DISMISSED"}, {"body": ""}): + with self.subTest(changed=changed): + payload = self.payload() + payload["main_sha"] = proposed + unchanged_policy = {"schema_version": 1, "max_custom_patch_lines": 0} + payload["policies_by_sha"] = { + "b" * 40: { + **unchanged_policy, + "next_policy": {"repository": "openai/example", "main_sha": previous}, + }, + "a" * 40: { + **unchanged_policy, + "next_policy": {"repository": "openai/example", "main_sha": proposed}, + }, + previous: unchanged_policy, + proposed: unchanged_policy, + } + payload["checker_sources_by_sha"] = { + previous: "NEXT_POLICY_VERSION = 1\n", + proposed: Path(policy.__file__).read_text(), + } + payload["review_nodes"] = [{**self.approval(), **changed}] + writes = self.invoke(payload) + self.assertCountEqual( + [(item["sha"], item["state"]) for item in writes if "state" in item], + [("a" * 40, "pending")] * 2 + [("c" * 40, "pending")] * 2, + ) + self.assertCountEqual( + [item["run_id"] for item in writes if "run_id" in item], [12, 14] + ) + + def test_unknown_or_missing_checker_contract_retains_review(self) -> None: + previous, proposed = "e" * 40, "f" * 40 + for source in ( + None, + 'PIN_REVIEW_POLICY = "unknown"\n', + 'PIN_REVIEW_POLICY = "effective-policy-v1"\n' * 2, + ): + with self.subTest(source=source): + payload = self.payload() + payload["main_sha"] = proposed + unchanged = {"schema_version": 1, "max_custom_patch_lines": 0} + payload["policies_by_sha"] = { + "b" * 40: { + **unchanged, + "next_policy": {"repository": "openai/example", "main_sha": previous}, + }, + "a" * 40: { + **unchanged, + "next_policy": {"repository": "openai/example", "main_sha": proposed}, + }, + previous: unchanged, + proposed: unchanged, + } + payload["checker_sources_by_sha"] = {} if source is None else {previous: source} + self.assertEqual(len(self.invoke(payload)), 6) + for status in (403, 429, 500): + payload["checker_lookup_error"] = status + with self.assertRaises(subprocess.CalledProcessError): + self.invoke(payload) + def test_initial_pin_at_current_main_keeps_statuses(self) -> None: payload = self.payload() checker = "f" * 40 @@ -1499,8 +1563,10 @@ def test_initial_pin_at_current_main_keeps_statuses(self) -> None: **payload["policies_by_sha"]["b" * 40], "next_policy": {"repository": "openai/example", "main_sha": checker}, } + payload["checker_sources_by_sha"] = {checker: Path(policy.__file__).read_text()} self.assertEqual(self.invoke(payload), []) advanced_main = "e" * 40 + payload["checker_sources_by_sha"][advanced_main] = Path(policy.__file__).read_text() payload["policies_by_sha"][advanced_main] = payload["policies_by_sha"][checker] payload["main_sha_sequence"] = [checker, advanced_main] payload["main_sha"] = advanced_main